Chapter 12

Around and Around It Goes

IN THIS CHAPTER

check Creating program loops

check Formulating solutions to problems with loops

check Diagnosing loop problems

Chapter 8 has code to reverse the letters in a four-letter word that the user enters. In case you haven’t read Chapter 8 or you just don’t want to flip to it, here’s a quick recap of the code:

c1 = keyboard.findWithinHorizon(".",0).charAt(0);
c2 = keyboard.findWithinHorizon(".",0).charAt(0);
c3 = keyboard.findWithinHorizon(".",0).charAt(0);
c4 = keyboard.findWithinHorizon(".",0).charAt(0);

System.out.print(c4);
System.out.print(c3);
System.out.print(c2);
System.out.print(c1);

The code is just dandy for words with exactly four letters, but how do you reverse a five-letter word? As the code stands, you have to add two new statements:

c1 = keyboard.findWithinHorizon(".",0).charAt(0);
c2 = keyboard.findWithinHorizon(".",0).charAt(0);
c3 = keyboard.findWithinHorizon(".",0).charAt(0);
c4 = keyboard.findWithinHorizon(".",0).charAt(0);
c5 = keyboard.findWithinHorizon(".",0).charAt(0);

System.out.print(c5);
System.out.print(c4);
System.out.print(c3);
System.out.print(c2);
System.out.print(c1);

What a drag! You add statements to a program whenever the size of a word changes! You remove statements when the input shrinks! That can’t be the best way to solve the problem. Maybe you can command a computer to add statements automatically. (But then again, maybe you can’t.)

As luck would have it, you can do something that’s even better: You can write a statement once and tell the computer to execute the statement many times. How many times? You can tell the computer to execute a statement as many times as it needs to be executed.

That’s the big idea. The rest of this chapter has the details.

Repeating Instructions over and over Again (Java while Statements)

Here’s a simple dice game: Keep rolling two dice until you roll 7 or 11. Listing 12-1 has a program that simulates the action in the game, and Figure 12-1 shows two runs of the program.

image

FIGURE 12-1: Momma needs a new pair of shoes.

LISTING 12-1 Roll 7 or 11

import java.util.Random;
import static java.lang.System.out;

class SimpleDiceGame {

public static void main(String args[]) {
Random myRandom = new Random();
int die1 = 0, die2 = 0;

while (die1 + die2 != 7 && die1 + die2 != 11) {
die1 = myRandom.nextInt(6) + 1;
die2 = myRandom.nextInt(6) + 1;
out.print(die1);
out.print(" ");
out.println(die2);
}

out.print("Rolled ");
out.println(die1 + die2);
}
}

At the core of Listing 12-1 is a thing called a while statement (also known as a while loop). A while statement has the following form:

while (Condition) {
Statements
}

Rephrased in English, the while statement in Listing 12-1 would say

while the sum of the two dice isn’t 7 and isn’t 11
keep doing all the stuff in curly braces: {

}

The stuff in curly braces (the stuff that’s repeated over and over) is the code that gets two new random numbers and displays those random numbers’ values. The statements in curly braces are repeated as long as die1 + die2 != 7 && die1 + die2 != 11 keeps being true.

Each repetition of the statements in the loop is called an iteration of the loop. In Figure 12-1, the first run has 2 iterations, and the second run has 12 iterations.

When die1 + die2 != 7 && die1 + die2 != 11 is no longer true (that is, when the sum is either 7 or 11), the repeating of statements stops dead in its tracks. The computer marches on to the statements that come after the loop.

Following the action in a loop

To trace the action of the code in Listing 12-1, I’ll borrow numbers from the first run in Figure 12-1:

  • At the start, the values of die1 and die2 are both 0.
  • The computer gets to the top of the while statement and checks to see whether die1 + die2 != 7 && die1 + die2 != 11 is true. (See Figure 12-2.) The condition is true, so the computer takes the true path in Figure 12-3.

    The computer performs an iteration of the loop. During this iteration, the computer gets new values for die1 and die2 and prints those values on the screen. In the first run of Figure 12-1, the new values are 3 and 1.

  • The computer returns to the top of the while statement and checks to see whether die1 + die2 != 7 && die1 + die2 != 11 is still true. The condition is true, so the computer takes the true path in Figure 12-3.

    The computer performs another iteration of the loop. During this iteration, the computer gets new values for die1 and die2 and prints those values on the screen. In Figure 12-1, the new values are 4 and 3.

  • The computer returns to the top of the while statement and checks to see whether die1 + die2 != 7 && die1 + die2 != 11 is still true. Lo and behold! This condition has become false! (See Figure 12-4.) The computer takes the false path in Figure 12-3.

    The computer leaps to the statements after the loop. The computer displays Rolled 7 and ends its run of the program.

image

FIGURE 12-2: Two wrongs don’t make a right, but two trues make a true.

image

FIGURE 12-3: Paths through the code in Listing 12-1.

image

FIGURE 12-4: Look! I rolled a seven!

No early bailout

In Listing 12-1, when the computer finds die1 + die2 != 7 && die1 + die2 != 11 to be true, the computer marches on and executes all five statements inside the loop’s curly braces. The computer executes

die1 = myRandom.nextInt(6) + 1;
die2 = myRandom.nextInt(6) + 1;

Maybe (just maybe), the new values of die1 and die2 add up to 7. Even so, the computer doesn’t jump out in mid-loop. The computer finishes the iteration and executes

out.print(die1);
out.print(" ");
out.println(die2);

one more time. The computer performs the test again (to see whether die1 + die2 != 7 && die1 + die2 != 11 is still true) only after it fully executes all five statements in the loop.

Thinking about Loops (What Statements Go Where)

Here’s a simplified version of the card game Twenty-One: You keep taking cards until the total is 21 or higher. Then, if the total is 21, you win. If the total is higher, you lose. (By the way, each face card counts as a 10.) To play this game, you want a program whose runs look like the runs in Figure 12-5.

image

FIGURE 12-5: You win sum; you lose sum.

In most sections of this book, I put a program’s listing before the description of the program’s features. But this section is different. This section deals with strategies for composing code. So in this section, I start by brainstorming about strategies.

Finding some pieces

How do you write a program that plays a simplified version of Twenty-One? I start by fishing for clues in the game’s rules, spelled out in this section’s first paragraph. The big fishing expedition is illustrated in Figure 12-6.

image

FIGURE 12-6: Thinking about a programming problem.

With the reasoning in Figure 12-6, I need a loop and an if statement:

while (total < 21) {
//do stuff
}

if (total == 21) {
//You win
} else {
//You lose
}

What else do I need to make this program work? Look at the sample output in Figure 12-5. I need a heading with the words Card and Total. That’s a call to System.out.println:

System.out.println("Card Total");

I also need several lines of output — each containing two numbers. For example, in Figure 12-5, the line 6 14 displays the values of two variables. One variable stores the most recently picked card; the other variable stores the total of all cards picked so far:

System.out.print(card);
System.out.print(" ");
System.out.println(total);

Now I have four chunks of code, but I haven’t decided how they all fit together. Well, you can go right ahead and call me crazy. But at this point in the process, I imagine those four chunks of code circling around one another, like part of a dream sequence in a low-budget movie. As you may imagine, I’m not very good at illustrating circling code in dream sequences. Even so, I handed my idea to the art department folks at John Wiley & Sons, Inc., and they came up with the picture in Figure 12-7.

image

FIGURE 12-7: and where they stop, nobody knows.

Assembling the pieces

Where should I put each piece of code? The best way to approach the problem is to ask how many times each piece of code should be executed:

  • The program displays card and total values more than once. For example, in the first run of Figure 12-5, the program displays these values four times (first 8 8, and then 6 14, and so on). To get this repeated display, I put the code that creates the display inside the loop:

    while (total < 21) {
    System.out.print(card);
    System.out.print(" ");
    System.out.println(total);
    }

  • The program displays the Card Total heading only once per run. This display comes before any of the repeated number displays, so I put the heading code before the loop:

    System.out.println("Card Total");

    while (total < 21) {
    System.out.print(card);
    System.out.print(" ");
    System.out.println(total);
    }

  • The program displays You win or You lose only once per run. This message display comes after the repeated number displays. So I put the win/lose code after the loop:

    //Preliminary draft code -- NOT ready for prime time:
    System.out.println("Card Total");

    while (total < 21) {
    System.out.print(card);
    System.out.print(" ");
    System.out.println(total);
    }

    if (total == 21) {
    System.out.println("You win :-)");
    } else {
    System.out.println("You lose :-(");
    }

Getting values for variables

I almost have a working program. But if I take the code that I’ve developed for a mental test run, I face a few problems. To see what I mean, picture yourself in the computer’s shoes for a minute. (Well, a computer doesn’t have shoes. Picture yourself in the computer’s boots.)

You start at the top of the code shown in the previous section (the code that starts with the Preliminary draft comment). In the code’s first statement, you display the words Card Total. So far, so good. But then you encounter the while loop and test the condition total < 21. Well, is total less than 21, or isn’t it? Honestly, I’m tempted to make up an answer because I’m embarrassed about not knowing what the total variable’s value is. (I’m sure the computer is embarrassed, too.)

The variable total must have a known value before the computer reaches the top of the while loop. Because a player starts with no cards at all, the initial total value should be 0. That settles it. I declare int total = 0 at the top of the program.

But what about my friend, the card variable? Should I set card to zero also? No. There’s no zero-valued card in a deck (at least, not when I’m playing fair). Besides, card should get a new value several times during the program’s run.

Wait! In the previous sentence, the phrase several times tickles a neuron in my brain. It stimulates the inside a loop reflex. So I place an assignment to the card variable inside my while loop:

//This is a DRAFT -- still NOT ready for prime time:
int card, total = 0;

System.out.println("Card Total");

while (total < 21) {
card = myRandom.nextInt(10) + 1;

System.out.print(card);
System.out.print(" ");
System.out.println(total);
}

if (total == 21) {
System.out.println("You win :-)");
} else {
System.out.println("You lose :-(");
}

The code still has an error, and I can probably find the error with more computer role-playing. But instead, I get daring. I run this beta code to see what happens. Figure 12-8 shows part of a run.

image

FIGURE 12-8: An incorrect run.

Unfortunately, the run in Figure 12-8 doesn’t stop on its own. This kind of processing is called an infinite loop. The loop runs and runs until someone trips over the computer’s extension cord.

remember You can stop a program’s run dead in its tracks. Look for the tiny red rectangle at the top of Eclipse’s Console view. When you hover the cursor over the rectangle, the tooltip says Terminate. When you click the rectangle, the active Java program stops running and the rectangle turns gray.

From infinity to affinity

For some problems, an infinite loop is normal and desirable. Consider, for example, a real-time mission-critical application — air traffic control, or the monitoring of a heart-lung machine. In these situations, a program should run and run and run.

But a game of Twenty-One should end pretty quickly. In Figure 12-8, the game doesn’t end, because the total never reaches 21 or higher. In fact, the total is always zero. The problem is that my code has no statement to change the total variable’s value. I should add each card’s value to the total:

total += card;

Again, I ask myself where this statement belongs in the code. How many times should the computer execute this assignment statement? Once at the start of the program? Once at the end of the run? Repeatedly?

The computer should repeatedly add a card’s value to the running total. In fact, the computer should add to the total each time a card gets drawn. So the preceding assignment statement should be inside the while loop, right alongside the statement that gets a new card value:

card = myRandom.nextInt(10) + 1;
total += card;

With this revelation, I’m ready to see the complete program. The code is in Listing 12-2, and two runs of the code are shown in Figure 12-5.

LISTING 12-2 A Simplified Version of the Game Twenty-One

import java.util.Random;

class PlayTwentyOne {

public static void main(String args[]) {
Random myRandom = new Random();
int card, total = 0;

System.out.println("Card Total");

while (total < 21) {
card = myRandom.nextInt(10) + 1;
total += card;

System.out.print(card);
System.out.print(" ");
System.out.println(total);
}

if (total == 21) {
System.out.println("You win :-)");
} else {
System.out.println("You lose :-(");
}
}
}

If you’ve read this whole section, you’re probably exhausted. Creating a loop can be a lot of work. Fortunately, the more you practice, the easier it becomes.

Thinking about Loops (Priming)

I remember when I was a young boy. We lived on Front Street in Philadelphia, near where the El train turned onto Kensington Avenue. Come early morning, I’d have to go outside and get water from the well. I’d pump several times before any water would come out. Ma and Pa called it “priming the pump.”

These days I don’t prime pumps. I prime while loops. Consider the case of a busy network administrator. She needs a program that extracts a username from an email address. For example, the program reads

[email protected]

and writes

John

How does the program do it? Like other examples in this chapter, this problem involves repetition:

Repeatedly do the following:
Read a character.
Write the character.

The program then stops the repetition when it finds the @ sign. I take a stab at writing this program. My first attempt doesn’t work, but it’s a darn good start. It’s in Listing 12-3.

LISTING 12-3 Trying to Get a Username from an Email Address

/*
* This code does NOT work, but I’m not discouraged.
*/
import java.util.Scanner;

class FirstAttempt {

public static void main(String args[]) {
Scanner keyboard = new Scanner(System.in);
char symbol = ’ ’;

while (symbol != ’@’) {
symbol = keyboard.findWithinHorizon(".",0).charAt(0);
System.out.print(symbol);
}
System.out.println();

keyboard.close();
}
}

When you run the code in Listing 12-3, you get the output shown in Figure 12-9. The user types one character after another — the letter J, then o, then h, and so on. At first, the program in Listing 12-3 does nothing. (The computer doesn’t send any of the user’s input to the program until the user presses Enter.) After the user types a whole email address and presses Enter, the program gets its first character (the J in John).

image

FIGURE 12-9: Oops! Got the @ sign, too.

Unfortunately, the program’s output isn’t what you expect. Instead of just the username John, you get the username and the @ sign.

To find out why this happens, follow the computer’s actions as it reads the input [email protected]:Set symbol to ’ ’ (a blank space).

Is that blank space the same as an @ sign?
No, so perform a loop iteration.
Input the letter ’J’.
Print the letter ’J’.

Is that ’J’ the same as an @ sign?
No, so perform a loop iteration.
Input the letter ’o’.
Print the letter ’o’.

Is that ’o’ the same as an @ sign?
No, so perform a loop iteration.
Input the letter ’h’.
Print the letter ’h’.

Is that ’h’ the same as an @ sign?
No, so perform a loop iteration.
Input the letter ’n’.
Print the letter ’n’.

Is that ’n’ the same as an @ sign? //Here’s the problem.
No, so perform a loop iteration.
Input the @ sign.
Print the @ sign. //Oops!

Is that @ sign the same as an @ sign?
Yes, so stop iterating.

Near the end of the program’s run, the computer compares the letter n with the @ sign. Because n isn’t an @ sign, the computer dives right into the loop:

  • The first statement in the loop reads an @ sign from the keyboard.
  • The second statement in the loop doesn’t check to see whether it’s time to stop printing. Instead, that second statement just marches ahead and displays the @ sign.

After you’ve displayed the @ sign, there’s no going back. You can’t change your mind and undisplay the @ sign. So the code in Listing 12-3 doesn’t quite work.

Working on the problem

You learn from your mistakes. The problem with Listing 12-3 is that, between reading and writing a character, the program doesn’t check for an @ sign. Instead of doing “Test, Input, Print,” it should do “Input, Test, Print.” That is, instead of doing this:

Is that ’o’ the same as an @ sign?
No, so perform a loop iteration.
Input the letter ’h’.
Print the letter ’h’.

Is that ’h’ the same as an @ sign?
No, so perform a loop iteration.
Input the letter ’n’.
Print the letter ’n’.

Is that ’n’ the same as an @ sign? //Here’s the problem.
No, so perform a loop iteration.
Input the @ sign.
Print the @ sign. //Oops!

the program should do this:

Input the letter ’o’.
Is that ’o’ the same as an @ sign?
No, so perform a loop iteration.
Print the letter ’o’.

Input the letter ’n’.
Is that ’n’ the same as an @ sign?
No, so perform a loop iteration.
Print the letter ’n’.

Input the @ sign.
Is that @ sign the same as an @ sign?
Yes, so stop iterating.

This cycle is shown in Figure 12-10.

image

FIGURE 12-10: What the program needs to do.

You can try to imitate the following informal pattern:

Input a character.
Is that character the same as an @ sign?
If not, perform a loop iteration.
Print the character.

The problem is, you can’t put a while loop’s test in the middle of the loop:

//This code doesn’t work the way you want it to work:
{
symbol = keyboard.findWithinHorizon(".",0).charAt(0);
while (symbol != ’@’)
System.out.print(symbol);
}

You can’t sandwich a while statement’s condition between two of the statements that you intend to repeat. So what can you do? You need to follow the flow in Figure 12-11. Because every while loop starts with a test, that’s where you jump into the circle, First Test, and then Print, and then, finally, Input.

image

FIGURE 12-11: Jumping into a loop.

Listing 12-4 shows the embodiment of this “test, then print, then input” strategy.

LISTING 12-4 Nice Try, But …

/*
* This code almost works, but there’s one tiny error:
*/
import java.util.Scanner;

class SecondAttempt {

public static void main(String args[]) {
Scanner keyboard = new Scanner(System.in);
char symbol = ’ ’;

while (symbol != ’@’) {
System.out.print(symbol);
symbol = keyboard.findWithinHorizon(".",0).charAt(0);
}

System.out.println();

keyboard.close();
}
}

A run of the Listing 12-4 code is shown in Figure 12-12. The code is almost correct, but I still have a slight problem. Notice the blank space before the user’s input. The program races prematurely into the loop. The first time the computer executes the statements

System.out.print(symbol);
symbol = keyboard.findWithinHorizon(".",0).charAt(0);

the computer displays an unwanted blank space. Then the computer gets the J in John. In some applications, an extra blank space is no big deal. But in other applications, extra output can be disastrous.

image

FIGURE 12-12: The computer displays an extra blank space.

Fixing the problem

Disastrous or not, an unwanted blank space is the symptom of a logical flaw. The program shouldn’t display results before it has any meaningful results to display. The solution to this problem is called … (drumroll, please) … priming the loop. You pump findWithinHorizon(".",0).charAt(0) once to get some values flowing. Listing 12-5 shows you how to do it.

LISTING 12-5 How to Prime a Loop

/*
* This code works correctly!
*/
import java.util.Scanner;

class GetUserName {

public static void main(String args[]) {
Scanner keyboard = new Scanner(System.in);
char symbol;

symbol = keyboard.findWithinHorizon(".",0).charAt(0);

while (symbol != ’@’) {
System.out.print(symbol);
symbol = keyboard.findWithinHorizon(".",0).charAt(0);
}

System.out.println();

keyboard.close();
}
}

Listing 12-5 follows the strategy shown in Figure 12-13. First you get a character (the letter J in John, for example), and then you enter the loop. After you’re in the loop, you test the letter against the @ sign and print the letter if it’s appropriate to do so. Figure 12-14 shows a beautiful run of the GetUserName program.

image

FIGURE 12-13: The strategy in Listing 12-5.

image

FIGURE 12-14: A run of the code in Listing 12-5.

The priming of loops is an important programming technique. But it’s not the end of the story. In Chapters 14, 15, and 16, you can read about some other useful looping tricks.

tryitout Face Java’s loops head-on with these programming challenges.

LIVING LARGE

Write a program that repeatedly reads numbers from the user’s keyboard. The program stops looping when the user types a number that’s larger than 100.

ARE WE THERE YET?

Write a program that repeatedly prompts the user with the same question: Are we there yet? The program stops looping when the user replies with the uppercase letter Y or the lowercase letter y. Here’s a sample run:

Are we there yet? N
Are we there yet? n
Are we there yet? N
Are we there yet? N
Are we there yet? N
Are we there yet? y
Whew!

TALLY UP

Write a program that uses a loop to repeatedly read numbers from the keyboard. The program stops reading numbers when the user enters a negative number. The program reports the sum of the numbers, excluding the last (negative) number.

GUESS AGAIN

In Chapter 9, you write a Guessing Game program. The program compares whatever number the user enters with a number that the program generates randomly. The user wins if the two numbers are equal, and loses otherwise.

Modify this program as follows:

  • As in the original version, the program generates a number randomly only once, but …
  • … the program repeatedly asks the user for guesses until the user guesses correctly.

TWO IN A ROW

Write a program that repeatedly reads numbers from the user’s keyboard. The program stops looping when the user types the same number twice in a row. Here’s a sample run of the program:

5
13
21
5
4
5
5
Done!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.27.178