How to re-write an input loop to not contain code repetition? - java

I have the following code, which continues to ask the user to enter a letter as long as the letter is either "a" or "b":
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
String letter;
System.out.print("Enter a letter: ");
letter = scan.nextLine();
while(letter.equals("a") || letter.equals("b"))
{
System.out.println("You entered: " + letter);
System.out.print("Enter a letter: ");
letter = scan.nextLine();
}
}
}
But the following code is repeated twice:
System.out.print("Enter a letter: ");
letter = scan.nextLine();
Is there a way to make the above code only appear one time?

while (true) {
System.out.print("Enter a letter: ");
String letter = scan.nextLine();
if (!letter.equals("a") && !letter.equals("b"))
break;
System.out.println("You entered: " + letter);
}
This is the classic example of a loop that is neither naturally while-do nor do-while — it needs to exit from the middle, if you want the same behavior and also to reduce code duplication.
(Notice also that the variable declaration letter has been moved to an inner scope since it is no longer needed in the outer scope.  This is a small positive indication.)
As an alternative to while (true) some languages allow degenerate for-loop as in for(;;).
The below reverses the logic of the conditional loop exit test, at the expense of more control flow logic.
while (true) {
System.out.print("Enter a letter: ");
String letter = scan.nextLine();
if (letter.equals("a") || letter.equals("b")) {
System.out.println("You entered: " + letter);
continue;
}
break;
}
(There is no difference between these in efficiency terms — these are equivalent at the level of machine code.)

do while loop
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
String letter;
do{
System.out.print("Enter a letter: ");
letter = scan.nextLine();
System.out.println("You entered: " + letter);
}while(letter.equals("a") || letter.equals("b"));
}
}
It will loop once first, and then continue again if the statment is true.

You need to perform three sequential actions in a loop:
read the input entered by the user;
validate the input;
print the input, but only if it is valid.
That means that conditional logic must reside inside the loop before the statement that prints the input. And that makes the condition of the loop redundant. Instead, we can create an infinite loop with a break statement wrapped by a condition, that validates the input.
While loop
That's how it can be done using a while loop:
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String letter;
while (true) {
System.out.print("Enter a letter: ");
letter = scan.nextLine();
if (!letter.matches("[ab]")) break;
System.out.println("You entered: " + letter);
}
}
Method matches() that expects a regular expression as argument is used in the code above to simplify the termination condition.
For more information on regular expressions, take a look at this tutorial
For loop
Regarding the advice of utilizing a for loop - that's doable, but by its nature the task of reading the user input fits better in the concept of while loop because we don't know the amount of data in advance, and the iteration can terminate at any point in time.
Also note syntax of the for statement consists of three parts separated with a semicolon:
initialization expression - allow to define and initialize variables that would be used in the loop;
termination expression - condition which terminates the execution of the loop;
increment expression - defines how variables would change at each iteration step.
All these parts are optional, but the two semicolons ; inside the parentheses always have to be present.
Because as I've said earlier, the input needs to be validated inside the loop, we can't take advantage from the termination expression and increment expression.
For the sake of completeness, below I've provided the version of how it can be achieved using a for loop:
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
for (String letter = "a"; ;) {
System.out.print("Enter a letter: ");
letter = scan.nextLine();
if (!letter.matches("[ab]")) break;
System.out.println("You entered: " + letter);
}
}
The only advantage is that the scope of the variable letter was reduced. But approach of utilizing while loop is more readable and expressive.
Alternative approach
Another option is to preserve your initial structure of the code:
initialize the variable letter before the loop, at the same line where it is defined;
enter the loop if letter holds a valid input;
print the input and reassign the variable.
But in order to avoid duplication of the code line responsible for printing the prompt and reading the input will be extracted into a separate method.
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String letter = readInput(scan);
while (letter.matches("[ab]")) {
System.out.println("You entered: " + letter);
letter = readInput(scan);
}
}
public static String readInput(Scanner scan) {
System.out.print("Enter a letter: ");
return scan.nextLine();
}

As Bobulous mentioned, a do-while loop is another simple solution. If duplicating the conditional is still a deal-breaker for you, though, you can also create a function that returns a boolean, and, when true, prints the extra text.
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
String letter;
do
{
System.out.print("Enter a letter: ");
letter = scan.nextLine();
} while(inputIsAOrB(letter));
}
public static boolean inputIsAOrB(String input) {
if (input.equals("a") || input.equals("b"))
{
System.out.println("You entered: " + input);
return true;
}
else
{
return false;
}
}

A while loop with a break after executing your second print command will limit the code to only asking for a single input.
Scanner ct = new Scanner(System.in);
String input;
System.out.print("Please enter either 'a' or 'b': ");
input = ct.nextLine();
while(input.equals("a") || input.equals("b")){
System.out.println("You entered: " + input);
break;
}

You can also view the problem as generating and processing a stream of strings. The side effects in the generator may trigger some philosophical discussions, otherwise I think it is quite clean:
Stream.generate(() -> {
System.out.print("Enter a letter: ");
return scan.nextLine();
})
.takeWhile(str -> str.equals("a") || str.equals("b"))
.forEach(str -> System.out.println("You entered: " + str));
... which will run like this:
Enter a letter: a
You entered: a
Enter a letter: b
You entered: b
Enter a letter: c
Process finished with exit code 0

Simply
List<Character> expectedChars = new ArrayList<>();
expectedChars.add('a');
expectedChars.add('b');
while(!expectedChars.contains(line = scan.nextLine())) {
System.out.println("Not expected");
}
// Now has a expected char. Proceed.

I without using any break or if-else externally (I am using ternary operator though) within control loop, you can also use below :
Scanner scanner = new Scanner(System.in);
boolean flag=true;
while (flag) {
System.out.println("enter letter");
String bv = scanner.nextLine();
flag=bv.matches("a|b");
System.out.println(flag?"you entered"+bv:' ');
}
with for loop, it can be even simpler :
Scanner scanner = new Scanner(System.in);
for (boolean flag = true; flag;) {
System.out.println("enter letter");
String bv = scanner.nextLine();
flag = bv.matches("a|b");
System.out.println(flag ? "you entered" + bv : ' ');
}
Or if you ok for having whitspace for first run:
Scanner scanner = new Scanner(System.in);
String aa=" ";
String bv=null;
for (boolean flag = true; flag;aa=(flag?"you entered: "+bv:" ")) {
System.out.println(aa);
System.out.println("enter letter");
bv = scanner.nextLine();
flag = bv.matches("a|b");
}

You can easily run a while loop until letter = "a" or letter = "b". Code will start the while loop with intial "" value of the letter and get the new value by scanner. Then it check the new value of the letter before starting the next round of the loop.
package com.company;
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
String letter = "";
while(!(letter.equals("a") || letter.equals("b")))
{
System.out.print("Enter a letter: ");
letter = scan.nextLine();
System.out.println("You entered: " + letter);
}
}
}

Similar as previous answer, but from a code readability perspective i would create an array of valid characters instead and use in condition. That results in the more "text like" condition below reading "if not validCharacters contains letter":
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
List<String> validCharacters = List.of("a", "b");
while (true) {
System.out.print("Enter a letter: ");
String letter = scan.nextLine();
if (!validCharacters.contains(letter)) {
break;
}
System.out.println("You entered: " + letter);
}
}

Your code just needed a little tweak to make it work, although you can use many more efficient approaches but here it is:
import java.util.Scanner;
public class Main
{
public static void main(String[] args)
{
Scanner scan = new Scanner(System.in);
String letter = "a";
while(letter.equals("a") || letter.equals("b"))
{
System.out.print("Enter a letter: ");
letter = scan.nextLine();
System.out.println("You entered: " + letter);
}
}
}

Related

Exiting from while loop not working in java

I am new to java programming.I want to calculate the sum and want to exit the program if user enters "N" and again loop if user enters "Y".But,it is not getting me out of loop even I enter "N".
public class Program {
public static void main(String[] args) {
boolean a=true;
while (a) {
System.out.println("enter a number");
Scanner c=new Scanner(System.in);
int d=c.nextInt();
System.out.println("enter a number2");
Scanner ce=new Scanner(System.in);
int df=ce.nextInt();
int kk=d+df;
System.out.println("total sum is"+kk);
System.out.println("do you want to continue(y/n)?");
Scanner zz=new Scanner(System.in);
boolean kkw=zz.hasNext();
if(kkw) {
a=true;
}
else {
a=false;
System.exit(0);
}
}
}
I didnt know where I made the mistake? Is there any other way?
First of all, your a variable is true if scanner.hasNext() is true, leading to a being true with every input, including "N" which means, your while loop will keep on going until there are no more inputs.
Second of all, you could optimize your code the next way:
I suggest getting rid of a and kkw to make your code cleaner and shorter.
Use only one Scanner and define it outside of the loop. You don't need more than one Scanner for the same input. Also, initializing a Scanner with every loop is resource-consuming.
Use meaningful variable names. Programming should not only be efficient, but also easy to read. In this tiny code it's a minor issue but imagine having an entire program and, instead of adding features and bug-fixing, you had to search for the meaning of every variable.
Here's an optimized and working version of your code:
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("Enter a number");
int input1 = scanner.nextInt();
scanner.nextLine(); // nextInt() doesn't move to the next line
System.out.println("Enter a second number:");
int input2 = scanner.nextInt();
scanner.nextLine();
System.out.println("Total sum is " + (input1 + input2)); /* Important to
surround the sum with brackets in order to tell the compiler that
input1 + input2 is a calculation and not an appending of
"Total sum is "*/
System.out.println("Do you want to continue? (Y/N)");
if (scanner.hasNext() && scanner.nextLine().equalsIgnoreCase("n"))
break;
}
scanner.close();
try (Scanner in = new Scanner(System.in)) {
boolean done = false;
while (!done) {
System.out.println("enter first number");
int d = in.nextInt();
System.out.println("enter second number");
int df = in.nextInt();
int kk = d + df;
System.out.println(String.format("total sum is %d", kk));
System.out.println("do you want to continue(y/n)?");
String cont = in.next();
done = cont.equalsIgnoreCase("n");
}
} catch(Exception e) {
e.printStackTrace();
}

replaceAll and loops?

My Computer Science class assignment requires that I write a program which determines if a word or phrase is a palindrome (is the same forward and backwards, ie "noon"). As part of this, I have to write a method which removes all punctuation and spaces, so they are not counted in determining if it is a palindrome. It also runs on a loop, allowing the user to input as many phrases they want until they indicate they're done. My problem is that when the word/phrase entered contains a space, somehow it terminates the loop and doesn't allow more input. The program works just fine, as long as the input has no spaces. Here's my code:
In class RecursivePalindrome:
public String removePunctuation(String s){
s = s.replaceAll("\\.","");
s = s.replaceAll("!","");
s = s.replaceAll(",","");
s = s.replaceAll(" ","");
s = s.replaceAll("'","");
s = s.replaceAll("-","");
s = s.replaceAll("\\?","");
return s;
}
public boolean isPalindrome(String s) {
s = removePunctuation(s);
String firstChar = s.substring(0,1);
String lastChar = s.substring(s.length()-1);
if (s.length() == 1){
return true;
}
if (s.length() == 2 && firstChar.equalsIgnoreCase(lastChar)){
return true;
}
if (!firstChar.equalsIgnoreCase(lastChar)){
return false;
}
return isPalindrome(s.substring(1, s.length() - 1));
}
In class RecursivePalindromeTester:
public static void main(String[]args){
//Create objects
Scanner in = new Scanner(System.in);
RecursivePalindrome palindrome = new RecursivePalindrome();
//Output
for (String again = "Y"; again.equalsIgnoreCase("Y"); again = in.next())
{
//Prompt for input
System.out.println();
System.out.print("Enter a word or phrase: ");
String phrase = in.next();
//Output
if (palindrome.isPalindrome(phrase)){
System.out.println("This is a palindrome.");
}
else
System.out.println("This is not a palindrome.");
System.out.print("Another word or phrase? (Y/N): ");
}
}
The output should be:
"Enter word or phrase: <input>mom- mom!
This is a palindrome
Another word or phrase? (Y/N): <input>Y
Enter a word or phrase: <input>Dog?
This is not a palindrome
Another word or phrase? (Y/N): <input>N"
Terminate
But instead I get:
"Enter word or phrase: <input>mom- mom!
This is a palindrome
Another word or phrase? (Y/N):"
Terminate
I really have no idea why a space would cause the loop to terminate, especially since it doesn't do this with any other punctuation.
Totally agreed with #Ilya Bursov comment,
You should use in.nextLine() instead of in.next() , there are big difference between both methods
next() can read the input only till the space. It can't read two words separated by a space. Also, next() places the cursor in the same line after reading the input.
nextLine() reads input including space between the words (that is, it reads till the end of line \n). Once the input is read, nextLine() positions the cursor in the next line
Try like this ,
class RecursivePalindromeTester {
public static void main(String[] args) {
//Create objects
Scanner in = new Scanner(System.in);
RecursivePalindrome palindrome = new RecursivePalindrome();
//Output
for (String again = "Y"; again.equalsIgnoreCase("Y"); again = in.nextLine()) {
//Prompt for input
System.out.println();
System.out.print("Enter a word or phrase: ");
String phrase = in.nextLine();
//Output
if (palindrome.isPalindrome(phrase)) {
System.out.println("This is a palindrome.");
}
else
System.out.println("This is not a palindrome.");
System.out.print("Another word or phrase? (Y/N): ");
}
}
}

Reversing the words in a sentence using Stack

Does my reverse method only work if I input a series of words all at once?
My task was to: Write a complete method that reads a series of Strings from the user. The user enters "end" to stop inputting words. Then, output the Strings in reverse order of how they were entered. Do not output the String “end”.
Use a stack to accomplish this task. Invoke only the methods push, pop, peek, and isEmpty on the stack object.
Here is how it is supposed to run:
Enter a word or 'end' to quit: Hello
Enter a word or 'end' to quit: Java
Enter a word or 'end' to quit: World
Enter a word or 'end' to quit: end
You entered (in reverse):
World
Java
Hello
But mine runs:
Enter a word or 'end' to quit: Hello
Enter a word or 'end' to quit: Java
Enter a word or 'end' to quit: World
Enter a word or 'end' to quit: end
You entered (in reverse): end
Here is what I have so far:
import java.util.Scanner;
import java.util.Stack;
import java.util.regex.Pattern;
public class Stack1 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = "end";
do {
System.out.printf("Enter a word or 'end' to quit: ");
input = scanner.nextLine();
if (input == null || input.length() == 0) {
System.out.println("Invalid! Try again...");
return;
}
} while(!input.equalsIgnoreCase("end"));
String reverse = reverse(input);
System.out.printf("You entered (in reverse): %s", reverse);
}
private static String reverse(String inputString) {
String[] str = inputString.trim().split(Pattern.quote(" "));
Stack stack = new Stack();
for(String input : str) {
stack.push(input);
}
StringBuilder builder = new StringBuilder();
while( !stack.isEmpty()) {
builder.append(stack.pop()).append(" ");
}
return builder.toString();
}
}
read input.
push it in stack.
if input equals "end" then stop reading input.
pop stack until stack gets empty.
Code
import java.util.Scanner;
import java.util.Stack;
public class Stack1 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String input = "";
Stack stack = new Stack();
while(true){
System.out.printf("Enter a word or 'end' to quit: ");
input = in.next(); // to read a word use next() method.
if(input.equalsIgnoreCase("end")){ break; }
if(!input.equals("")) stack.push(input);
}
System.out.println("You entered (in reverse): ");
while(!stack.isEmpty())
System.out.println(stack.pop());
}
}
Your loop is overwriting input on each iteration. To make it work with your reverse() method, you'll want to concat each word incrementally with a space:
String input = "";
while (true) {
System.out.printf("Enter a word or 'end' to quit: ");
String next = scanner.nextLine();
if (next == null || next.length() == 0) {
System.out.println("Invalid! Try again...");
return;
}
if (next.equalsIgnoreCase("end")) {
break;
}
input += next + " ";
}
Alternatively, you can populate the stack directly in the loop and skip the string splitting:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Stack<String> stack = new Stack<>();
while (true) {
System.out.printf("Enter a word or 'end' to quit: ");
String next = scanner.nextLine();
if (next == null || next.length() == 0) {
System.out.println("Invalid! Try again...");
return;
}
if (next.equalsIgnoreCase("end")) {
break;
}
stack.push(next);
}
System.out.println("You entered (in reverse):");
while (!stack.isEmpty()) {
System.out.println(stack.pop());
}
}
Note that the latter solution correctly reverses multi-word inputs, whereas the concatenation approach can't differentiate between lines and words.
This is because your input variable contains only "end". Thus whenever you call reverse function ,it only reverses end String.
String reverse = reverse(input);//input="end"
the problem comes from this place
#Toby Speight thanks for your advice.
by the way I am a English newcomer,I'm pleased to accept any suggestion.
What I'm trying to say is to learn debug your program when you meet problems.In this case: you want the program print a reversed String to the console .but you got a strange answer.now you need to consider where the answer maybe comes from ?
and then just print it before you use it like below
String reverse = reverse(input);//we said you think the problem comes from this place.
System.out.printf("%s%n",input);//this is a key statement to debug--just print it
Now you and then consider where input is not what you want? and then just consider where input comes from?keep on doing this ,and then you can find where
your problem comes from.
On the other hand ,there have a lot of method to debug,you can use IDE debug your program and it will be more efficient ,what's more you can use a log file and so on.
You're using a do-while loop, which is why "end" gets added to the array.
To reverse the string, you can either use a for loop and reverse it yourself:
StringBuilder reverseStr = new StringBuilder();
for (int i = str.size - 1; i >= 0; i--) {
reverseStr.append(str[i]);
}
Or use something like Apache Commons Lang StringUtils.reverse().

Java looping issue

What this program does is takes words entered by the user and returns them in pig-latin form. The translation loop continues until the user enters "quit". My problem is that while the program executes and translates the words, after the word quit is entered the it translates "quit" which I don't want it to do. I know that the reason that it translates "quit" before finishing is that it's a do while loop but I'm stuck on how create a while loop that functions. How would I alter the program so that "quit" is what terminates the loop and isn't translated?
Example:
Word: quit
uit-qay
import java.util.Scanner;
public static void main(String[] args) {
String word;
Scanner input = new Scanner(System.in);
do {
System.out.print("Word: ");
word = input.next();
System.out.println(pigLatinWord(word));
System.out.println();
} while (!word.equalsIgnoreCase("quit"));
System.out.println("Translation complete");
}
// --------------------------------------------------------
// Convert one word to pig Latin.
public static String pigLatinWord(String s) {
String pigWord;
if (isVowel(s.charAt(0))) {
pigWord = s + "-way";
} else if (s.startsWith("th") || s.startsWith("Th")) { // or
// (s.toUpperCase().startsWith("TH"))
pigWord = s.substring(2) + "-" + s.substring(0, 2) + "ay";
} else {
pigWord = s.substring(1) + "-" + s.charAt(0) + "ay";
}
return pigWord;
}
// ---------------------------------------------
// Determines whether c is a vowel character
public static boolean isVowel(char c) {
String vowels = "aeiouAEIOU";
return (vowels.indexOf(c) >= 0); // when index of c in vowels is not -1,
// c is a vowel
}
}
You're executing pigLatinWord(word) before you get a chance to check if the word equals "quit". You can change the loop as such:
do {
System.out.print("Word: ");
word = input.next();
if( "quit".equalsIgnoreCase(word) )
break;
System.out.println(pigLatinWord(word));
System.out.println();
} while (true);
do {} while (); is generally bad to use, try to use while () {} instead. Like this:
Scanner input = new Scanner(System.in);
boolean shouldQuit = false;
while (!shouldQuit) {
System.out.print("Word: ");
word = input.next();
if (word.equalsIgnoreCase("quit")) {
shouldQuit = true;
} else {
System.out.println(pigLatinWord(word));
System.out.println();
}
}
System.out.println("Translation complete");
Or if you want to stick with do {} while, see the other answer.
This is one possible way. Though, it'll involve not using a do-while.
//This is an infinite loop, except that we have our exit condition inside the
//body that'll forcibly break out of the loop.
while (true) {
System.out.print("Word: ");
word = input.next();
if (word.equalsIgnoreCase("quit")) {
break; //Whelp! The user wants to quit. Break the loop NOW
}
System.out.println(pigLatinWord(word));
System.out.println();
}
System.out.println("Translation complete");
This one works, i tried it :)
String word;
Scanner input = new Scanner(System.in);
System.out.print("Word: ");
while(!(word = input.next()).equalsIgnoreCase("quit")) {
System.out.print("Word: ");
System.out.println(pigLatinWord(word));
System.out.println("fsfafa");
}
System.out.println("Translation complete");

why is my program ignoring my if else statement?

My program should output ‘What is your name?’ only if “hi” and “hello” are typed, but it still outputs ‘What is your name?’ even if I type a number or a single character… I’m so frustrated. Can someone help me out with this? I think it has something to do with the !phrase.equals statement…
import java.util.Scanner;
public class prog2 {
public static void main(String[] args)throws Exception {
String phrase;
String name;
char Letter;
Scanner keyboard = new Scanner(System.in);
System.out.print("Type a phrase: ");
phrase = keyboard.nextLine();
if(!phrase.equals("hi") || !phrase.equals("hello")){
System.out.print("What is your name?");
Scanner keyboard1 = new Scanner(System.in);
name = keyboard1.nextLine();
System.out.print("Your name is" +name);
}else{
Scanner keyboard2 = new Scanner(System.in);
System.out.print("Type a Letter: ");
Letter = (char) System.in.read();
System.in.read();
System.out.print("Your letter is "+ Letter);
}
}
You condition !phrase.equals("hi") || !phrase.equals("hello") is always true.
If the word is hi, it's false or true; if the word is hello, it's true or false. Otherwise its true or true. You did not describe the intended behaviour, thus I can't tell what is correct.
It's not doing what you wanted to do. Check De Morgan laws.
You probably want to do:
if(!(phrase.equals("hi") || phrase.equals("hello")))
Which is not like what you did because if(!a || !b) is not like if(!(a || b)).
Try :
if(phrase.equals("hi") || phrase.equals("hello")) {
//rest of your code
}

Categories