How about a readln for numbers?
Ethan McCue
ethan at mccue.dev
Thu Nov 7 18:07:33 UTC 2024
void main() {
int number;
do {
String input = readln("Enter a number between 1 and 10: ");
try {
number = Integer.parseInt(input);
if (number < 1 || number > 10) {
println("Number out of range");
}
// All is well
break;
} catch (NumberFormatException _) {
println("You have not entered a number");
}
} while (true);
println(number);
}
For color on how that might affect the pedagogy:
"First we need to get input from the user, this is a String which
represents text"
void main() {
String input = readln("Enter a number between 1 and 10: ");
}
"We can turn a String into an int by writing Integer.parseInt(<text here>).
If what they wrote isn't a number it will crash"
void main() {
String input = readln("Enter a number between 1 and 10: ");
int number = Integer.parseInt(input);
}
"To handle crashes we can put the code that might crash inside of try { },
then write the way it might crash in a catch clause"
void main() {
String input = readln("Enter a number between 1 and 10: ");
try {
int number = Integer.parseInt(input);
println("You gave: " + number);
} catch (NumberFormatException e) {
println("Bad number: " + input);
}
}
"If they gave a number that wasn't between 1 and 10 ..."
void main() {
String input = readln("Enter a number between 1 and 10: ");
try {
int number = Integer.parseInt(input);
if (number < 1 || number > 10) {
println("Number is not between 1 and 10: " + number);
}
else {
println("You gave: " + number);
}
} catch (NumberFormatException e) {
println("Bad number: " + input);
}
}
"Now we want to keep asking them until they give a valid input. To do this,
first delay the assignment of number"
void main() {
String input = readln("Enter a number between 1 and 10: ");
int number;
try {
number = Integer.parseInt(input);
if (number < 1 || number > 10) {
println("Number is not between 1 and 10: " + number);
}
else {
println("You gave: " + number);
}
} catch (NumberFormatException e) {
println("Bad number: " + input);
}
}
... and so on until you reach
void main() {
String input = readln("Enter a number between 1 and 10: ");
int number;
do {
try {
number = Integer.parseInt(input);
if (number < 1 || number > 10) {
println("Number is not between 1 and 10: " + number);
}
else {
break;
}
} catch (NumberFormatException e) {
println("Bad number: " + input);
}
} while (true);
println("You gave: " + number);
}
On Thu, Nov 7, 2024 at 12:50 PM Ethan McCue <ethan at mccue.dev> wrote:
> So with all that context, do you see why accepting
> Double.parseDouble(readln()); might be worth it?
>
> "Paving the onramp" shouldn't mean only "paving the first 48 hours." The
> convenience afforded by a dedicated readDouble, at least to me, feels
> outweighed
> by
>
> * The loss of a perfectly good opportunity to explain the fundamentals of
> parsing (strings represent text, you can interpret that text by....)
> * The loss of a perfectly good opportunity to teach basic exception
> handling
> * The divergence with the behavior of Scanner
> * The combinatorial explosion of "why not readByte?"
> * The privileged position it puts primitives in as the end-point of
> interpreting user input
> * Guns, Feet
>
> (I've separately voiced my concerns about readln in this regard.)
>
> On Thu, Nov 7, 2024 at 12:40 PM Kenneth Fogel <kfogel at dawsoncollege.qc.ca>
> wrote:
>
>> Here is an example of a Beethoven test passing routine that expects a
>> number for 1 to 10. The Scanner, sc, has already been initialized. I also
>> taught how to use regular expressions to fine tune what is acceptable input
>> and the acceptable range such that you only need to use readLine so that
>> subsequent input is not messed up when a user enters 23 45 instead of
>> 23.45. Notice that this routine cleans out the buffer with a nextLine at
>> the end.
>>
>>
>>
>> int number;
>>
>> do {
>>
>> System.out.println("Enter a number between 1 and 10: ");
>>
>> if (sc.hasNextInt()) { // Check that there is an integer in
>> the keyboard buffer
>>
>> number = sc.nextInt(); //
>>
>> // Check if the number is in range
>>
>> if (number < 1 || number > 10) {
>>
>> System.out.println("Number out of range.");
>>
>> }
>>
>> } else { // There was not an integer in the keyboard buffer
>>
>> number = -1; // a value that will keep execution in the
>> loop
>>
>> System.out.println("You have not entered a number");
>>
>> }
>>
>> sc.nextLine(); // Clean out the buffer
>>
>> } while (number < 1 || number > 10);
>>
>>
>>
>> This tells me that in the paving the onramp universe a readInt or readDbl
>> must clear the keyboard buffer when it encounters a terminating character
>> such as the space, tab, and \n. Already I can hear the roar over how this
>> breaks the expected behaviour of Scanner such as allowing a list of
>> primitives in the keyboard buffer.
>>
>>
>>
>> Ken
>>
>>
>>
>>
>>
>> *From:* Ethan McCue <ethan at mccue.dev>
>> *Sent:* November 7, 2024 11:43 AM
>> *To:* Kenneth Fogel <kfogel at dawsoncollege.qc.ca>
>> *Cc:* discuss at openjdk.org
>> *Subject:* Re: How about a readln for numbers?
>>
>>
>>
>> Currently, "don't mix nextLine with next/next int/etc" is an extremely
>> common footgun.
>>
>> In one of the coding help discords, this is the auto message we send when
>> people run into trouble with that.
>>
>> Mixing any nextXXX method with nextLine from the Scanner class for user
>> input, will not ask you for input again but instead result in an empty line
>> read by nextLine. To prevent this, when reading user input, always only
>> use nextLine. If you need an int, do
>>
>> int value = Integer.parseInt(scanner.nextLine());
>>
>> instead of using nextInt. Assume the following:
>>
>> Scanner scanner = new Scanner(System.in);
>>
>>
>>
>> System.out.println("Enter your age:");
>>
>> int age = scanner.nextInt();
>>
>> System.out.println("Enter your name:");
>>
>> String name = scanner.nextLine();
>>
>>
>>
>> System.out.println("Hello " + name + ", you are " + age + " years old");
>>
>> When executing this code, you will be asked to enter an age, suppose you
>> enter 20. However, the code will not ask you to actually input a name
>> and the output will be:
>>
>> Hello , you are 20 years old.
>>
>> The reason why is that when you hit the enter button, your actual input
>> is
>>
>> 20\n
>>
>> and not just 20. A call to nextInt will now consume the 20 and leave the
>> newline symbol \n in the internal input buffer of System.in. The call to
>> nextLine will now not lead to a new input, since there is still unread
>> input left in System.in. So it will read the \n, leading to an empty
>> input. So every user input is not only a number, but a *full line*. As
>> such, it makes much more sense to also use nextLine(), even if reading
>> just an age. The corrected code which works as intended is:
>>
>> Scanner scanner = new Scanner(System.in);
>>
>>
>>
>> System.out.println("Enter your age:");
>>
>> // Now nextLine, not nextInt anymore
>>
>> int age = Integer.parseInt(scanner.nextLine());
>>
>> System.out.println("Enter your name:");
>>
>> String name = scanner.nextLine();
>>
>>
>>
>> System.out.println("Hello " + name + ", you are " + age + " years old");
>>
>> The nextXXX methods, such as nextInt can be useful when reading
>> multi-input from a single line. For example when you enter 20 John in a
>> single line.
>>
>>
>>
>>
>>
>> On Thu, Nov 7, 2024, 11:36 AM Kenneth Fogel <kfogel at dawsoncollege.qc.ca>
>> wrote:
>>
>> The readln method introduced as part java.base is great because it
>> includes the prompt for the input. The only shortcoming is that if the
>> input must be a number then you still need to employ a static class member
>> such as Double.parseDouble() to make it a number:
>>
>>
>>
>> // Currently
>>
>> var loan = Double.parseDouble(readln("Loan: "));
>>
>>
>>
>> // My delusional idea
>>
>> var loan = readDbl("Loan: ");
>>
>>
>>
>> I can think of reasons why this could be a bad idea. I taught my students
>> to use a construct using Scanner to create a console input that passed my
>> Beethoven test (while humming Beethoven’s 5th symphony randomly strike
>> keys on your keyboard as if you were playing the symphony and your code
>> should just report invalid input and not an exception). If we had a readInt
>> and/or readDbl they would not pass this test. That Python would happily
>> accept anything and then fail when the value was used in a calculation is
>> no better. But, for learning Java could we have a readInt or readDbl
>> alongside readln? I know that DataInputStream has such methods, but
>> without a prompt and it must be attached to an input stream such as a file.
>>
>>
>>
>> As always, just thinking out loud. Feel free to use my name in vain.
>>
>>
>>
>> Ken
>>
>>
>>
>>
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/discuss/attachments/20241107/8d11b8e0/attachment-0001.htm>
More information about the discuss
mailing list