Iteration: Chapter Goals

Download as pdf or txt
Download as pdf or txt
You are on page 1of 46

Iteration

6
CHAPTER

Chapter Goals
◆ To be able to program Loops with the while, for, and do statements

◆ To learn how to read input from the console and from a file through redirection

◆ To learn how to read string input

◆ To implement simulations

◆ To avoid infinite loops and off-by-one errors

◆ To understand nested loops

223
224 Chapter 6. Iteration

6.1 while Loops

Recall the investment problem from Chapter 1. You put $10,000 into a bank account
that earns 5 percent interest per year. How many years does it take for the account
balance to be double the original?
In Java, the while statement implements such a repetition. The code
while ( condition )
statement
keeps executing the statement while the condition is true. Most commonly, the state-
ment is a block statement, that is, a set of statements delimited by { . . . }.
Here is the program that solves our investment problem:

Program DoubleInv.java
public class DoubleInv
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);

System.out.println("Interest rate:");
double rate = console.readDouble();

System.out.println("Initial balance:");
double initialBalance = console.readDouble();

int year = 0;
double balance = initialBalance;

// keep accumulating interest until balance doubles

while (balance < 2 * initialBalance)


{ year++;
double interest = balance * rate / 100;
balance = balance + interest;
}

System.out.println("The investment doubled after "


+ year + " years.");
}
}
A while statement is often called a loop. If you draw a flowchart, you will see that
the control loops backwards to the test after every iteration (see Figure 1).
The following loop,
while (true)
{ body
}
6.1 while Loops 225

Java Syntax
6.1 The while Statement
while (condition)
statement
Example:
while (balance < 2 * initialBalance)
{ year++;
double interest = balance * rate / 100;
balance = balance + interest;
}

Purpose:
To execute a statement while a condition is true

Figure 1

Flowchart of a while Loop year = 0

balance < 2 * No
initialBalance?

Yes

Add interest;
year++

Print year
226 Chapter 6. Iteration

executes the body over and over, without ever terminating. Whoa! Why would you
want that? The program would never stop. There are two reasons. Some programs
indeed never stop; the software controlling an automated teller machine, a tele-
phone switch, or a microwave oven doesn’t ever stop (at least not until the device
is turned off). Our programs are not usually of that kind, but even if you can’t ter-
minate the loop, you can exit from the method that contains it. This can be helpful
when the termination test naturally falls into the middle of the loop (see Advanced
Topic 6.3).

Common Error 6.1


◆ Infinite Loops

◆ The most annoying loop error is an infinite loop: a loop that runs forever and can be stopped
◆ only by killing the program or restarting the computer. If there are output statements in the
◆ loop, then reams and reams of output flash by on the screen. Otherwise, the program just sits
◆ there and hangs, seeming to do nothing. On some systems you can kill a hanging program
◆ by hitting Ctrl+Break or Ctrl+C. On others, you can close the window in which the program
◆ runs.
◆ A common reason for infinite loops is forgetting to advance the variable that controls the
◆ loop:

◆ int year = 0;
◆ while (year < 20)
◆ { double interest = balance * rate / 100;
◆ balance = balance + interest;
◆ }

◆ Here the programmer forgot to add a year++ command in the loop. As a result, the year always
◆ stays 0, and the loop never comes to an end.
◆ Another common reason for an infinite loop is accidentally incrementing a counter that
◆ should be decremented (or vice versa). Consider this example:

◆ int year = 20;
◆ while (year > 0)
◆ { year++; // Oops, should have been year--
◆ double interest = balance * rate / 100;
◆ balance = balance + interest;
◆ }

◆ The year variable really should have been decremented, not incremented. This is a common
◆ error, because incrementing counters is so much more common than decrementing that your
◆ fingers may type the ++ on autopilot. As a consequence, year is always larger than 0, and the
◆ loop never terminates. (Actually, eventually year will exceed the largest representable positive
◆ integer and wrap around to a negative number. Then the loop exits—of course, that takes a long
◆ time, and the result is completely wrong.)
6.2 for Loops 227

6.2 for Loops

Far and away the most common loop has the form
i = start ;
while (i <= end )
{ . . .
i++;
}
Because this loop is so common, there is a special form for it that emphasizes the
pattern:
for (i = start ; i <= end ; i++)
{ . . .
}
You can also define the loop counter variable inside the for loop header. That is a
convenient shorthand. It restricts the use of the variable to the body of the loop.
for (int i = start ; i <= end ; i++)
{ . . .
}
Here is a program that prints out the growth of an investment over 20 years:

Program Invest.java
public class Invest
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);

System.out.println("Interest rate:");
double rate = console.readDouble();

final double INITIAL_BALANCE = 10000;


final int NYEARS = 20;

double balance = INITIAL_BALANCE;

// accumulate interest for NYEARS

for (int year = 1; year <= NYEARS; year++)


{ double interest = balance * rate / 100;
balance = balance + interest;
System.out.println("year: " + year +
" balance: " + balance);
}
}
}
Figure 2 shows the corresponding flowchart.
228 Chapter 6. Iteration

Java Syntax
6.2 The for Statement
for (initialization ; condition ; update)
statement

Example:
for (i = 1; i <= 20; i++)
{ double interest = balance * rate / 100;
balance = balance + interest;
}

Purpose:
To execute an initialization, then keep executing
a statement and updating an expression while a
condition is true

year = 1

No
year <= 20?

Yes

Add interest

Print year,
balance

year++

Figure 2

Flowchart of a for Loop


6.2 for Loops 229

The three slots in the for header can contain any three expressions. You can count
down instead of up:
for (year = 20; year > 0; year--)
The increment or decrement need not be in steps of 1:
for (x = -10; x <= 10; x = x + 0.5) . . .
It is possible—but a sign of unbelievably bad taste—to put unrelated conditions into
the loop:
for (rate = 6; year-- > 0; System.out.println(balance))
. . . // Bad taste
We won’t even begin to decipher what that might mean; look in Feuer [1] for puzzles
of this kind. You should stick with for loops that initialize, test, and update a single
variable.

Quality Tip 6.1


◆ Use for Loops for Their Intended Purpose Only

◆ A for loop is an idiom for a while loop of a particular form. A counter runs from the start to
◆ the end, with a constant increment:
◆ for (set counter to start; test whether counter at end;
◆ update counter by increment)
◆ { . . .
◆ // counter, start, end, increment not changed here
◆ }

◆ If your loop doesn’t match this pattern, don’t use the for construction. The compiler won’t
◆ prevent you from writing idiotic for loops:
◆ // bad style—unrelated header expressions
◆ for (System.out.println("Inputs:");
◆ (x = console.readDouble()) > 0;
◆ sum = sum + x)
◆ count++;

◆ for (year = 1; year <= 20; year++)
◆ { // bad style—modifies counter

◆ if (balance >= 2 * initialBalance)
◆ year = 20;
◆ else
◆ { double interest = balance * rate / 100;

balance = balance + interest;

}

}

◆ These loops will work, but they are plainly bad style. Use a while loop for iterations that do
◆ not fit into the for pattern.
230 Chapter 6. Iteration

Advanced Topic 6.1


◆ Scope of Variables Defined in a for
◆ Loop Header

◆ It is legal in Java to declare a variable in the header of a for loop. Here is the most common
◆ form of this syntax:

◆ for (int year = 1; year <= 10; year++)
◆ { . . .
◆ }


// year no longer defined here


The scope of the variables extends to the end of the for loop. That means, year is no longer

defined after the loop ends. If you need to use the value of the variable beyond the end of

the loop, then you need to define it outside the loop. In this loop, you don’t need the value

◆ of year—you know it is 10 when the loop is finished. (Actually, that is not quite true—it is
◆ possible to break out of a loop before its end; see Advanced Topic 6.3). When you have two
◆ or more exit conditions, though, you may still need the variable. For example, consider the
◆ loop

◆ for (int year = 1; balance < 2 * initialBalance && year <= 10;
◆ year++)
◆ { . . .
◆ }

◆ You want the balance to double, but you are only willing to wait ten years. If the balance
◆ doubled earlier, you may want to know the value of year. Therefore, in this case, it is not
◆ appropriate to define the variable in the loop header.
◆ Note that the variables named i in the following pair of for loops are independent:

◆ public static void main(String[] args)
◆ { for (int i = 1; i <= 10; i++)
◆ System.out.println(i * i);
◆ for (int i = 1; i <= 10; i++) // not a redefinition of i
◆ System.out.println(i * i * i);
◆ }

◆ You can declare multiple variables, as long as they are of the same type, and you can include
◆ multiple update expressions, separated by commas:

◆ for (int i = 0, j = 10; i <= 10; i++, j--)
◆ { . . .
◆ }

◆ However, many people find it confusing if a for loop controls more than one variable. We
◆ recommend that you don’t use this form of the for statement (see Quality Tip 6.1). Instead,
◆ make the for loop control a single counter, and update the other variable explicitly:
6.3 do Loops 231

int j = 10;

for (int i = 0; i <= 10; i++)
◆ { . . .
◆ j--;
◆ }

6.3 do Loops

Sometimes you want to execute the body of a loop at least once and perform the
loop test after the body was executed. The do loop serves that purpose:
do
statement
while (condition );
The statement is executed while the condition is true. Since the condition is tested after
the statement is executed, the statement is executed at least once.
For example, suppose you want to make sure that a user enters a positive number.
As long as the user enters a negative number or zero, just keep prompting for a correct
input. In this situation, a do loop makes sense because you need to get a user input
before you can test it.
do
{ System.out.println("Please enter an interest rate (> 0):");
rate = console.readDouble();
} while (rate <= 0);
See Figure 3 for a flowchart.

Figure 3

Flowchart of a do Loop

Print prompt

Read rate

Yes
rate <= 0?

No
232 Chapter 6. Iteration

Java Syntax
6.3 The do Statement
do
statement
while (condition );

Example:
do
{ System.out.println("Please enter an interest rate (> 0):");
rate = console.readDouble();
} while (rate <= 0);

Purpose:
To execute a statement once and then keep executing it as long as a
condition is true

Like a for loop, a do loop can always be replaced by a while loop. Here we
initialize rate with an artificial value to ensure that the test passes the first time
around:
double rate = -1;
while (rate <= 0)
{ System.out.println("Please enter an interest rate (> 0):");
rate = console.readDouble();
}
This artificial choice is not good style, and it is best to use a do loop in this case.

Common Error 6.2


◆ Off-by-1 Errors

◆ Consider our computation of the number of years that are required to double an invest-
◆ ment:
◆ int year = 0;
◆ while (balance < 2 * initialBalance)
◆ { year++;
◆ double interest = balance * rate / 100;
◆ balance = balance + interest;
◆ }

System.out.println("The investment doubled after "

+ year + " years.");

◆ Should year start at 0 or at 1? Should you test for balance < 2 * initialBalance or for
◆ balance <= 2 * initialBalance? It is easy to be off by 1 in these expressions.
6.3 do Loops 233

◆ Some people try to solve off-by-1 errors by randomly inserting + 1 or - 1 until the program
◆ seems to work. That is, of course, a terrible strategy. It can take a long time to compile and
◆ test all the various possibilities. Expending a small amount of mental effort is a real time saver.
◆ Fortunately, off-by-1 errors are easy to avoid, simply by thinking through a couple of test
◆ cases and using the information from the test cases to come up with a rationale for your
◆ decisions.
◆ Should year start at 0 or at 1? Look at a scenario with simple values: an initial balance
◆ of $100 and an interest rate of 50%. After year 1, the balance is $150, and after year 2 it is

$225, or over $200. So the investment doubled after 2 years. The loop executed two times,

incrementing year each time. Hence year must start at 0, not at 1.

In other words, the balance variable denotes the balance after the end of the year. At the

◆ outset, the balance variable contains the balance after year 0 and not after year 1.
◆ Next, should you use a < or <= comparison in the test? That is harder to figure out, because
◆ it is rare for the balance to be exactly twice the initial balance. Of course, there is one case
◆ when this happens, namely when the interest is 100%. The loop executes once. Now year is
◆ 1, and balance is exactly equal to 2 * initialBalance. Has the investment doubled after
◆ one year? It has. Therefore, the loop should not execute again. If the test condition is balance
◆ < 2 * initialBalance, the loop stops, as it should. If the test condition had been balance
◆ <= 2 * initialBalance, the loop would have executed once more.
◆ In other words, you keep adding interest while the balance has not yet doubled.

Common Error 6.3


◆ Forgetting a Semicolon

◆ It occasionally happens that all the work of a loop is already done in the loop header. Suppose
◆ you ignored Quality Tip 6.1. Then you could write the investment doubling loop as follows:

◆ for (year = 1; (balance = balance
◆ + balance * rate / 100) < 2 * initialBalance; year++)
◆ ;
◆ System.out.println("The investment doubled after "
◆ + year + " years.");

◆ The body of the for loop is completely empty, containing just one empty statement termi-
◆ nated by a semicolon.
◆ If you do run into a loop without a body, it is important that you really make sure the
◆ semicolon is not forgotten. If the semicolon is accidentally omitted, then the code repeats the
◆ print statement multiple times.

◆ for (year = 1; (balance = balance
◆ + balance * rate / 100) < 2 * initialBalance; year++)
◆ System.out.println("The investment doubled after "
◆ + year + " years.");

◆ To make the semicolon really stand out, place it on a line all by itself, as shown in the first
◆ example.
234 Chapter 6. Iteration

Common Error 6.4


◆ A Semicolon Too Many

◆ What does the following loop print?
◆ sum = 0;
◆ for (i = 1; i <= 10; i++);
◆ sum = sum + i;

System.out.println(sum);

◆ Of course, this loop is supposed to compute 1 + 2 + ⭈⭈⭈ + 10 = 55. But actually, the print
◆ statement prints 11!
◆ Why 11? Have another look. Did you spot the semicolon at the end of the for loop? This
◆ loop really is a loop with an empty body.

◆ for (i = 1; i <= 10; i++)
◆ ;

The loop does nothing ten times, and when it is finished, sum is still 0 and i is 11. Then the

◆ statement
◆ sum = sum + i;

◆ is executed, and sum is 11. The statement was indented, which fools the human reader. But
◆ the compiler pays no attention to indentation.
◆ Of course, the semicolon at the end of the statement was a typing error. Someone’s fingers
◆ were so used to typing a semicolon at the end of every line that a semicolon was added to the
◆ for loop by accident. The result was a loop with an empty body.

Quality Tip 6.2


◆ Don’t Use != to Test the End of a Range

◆ Here is a loop with a hidden danger:

for (i = 1; i != nyear; i++)

{ . . .

}

◆ The test i != nyear is a poor idea. What would happen if nyear happened to be negative? Of
◆ course, nyear should never be negative, because it makes no sense to have a negative number
◆ of years—but the impossible and unthinkable do happen with distressing regularity. If nyear
◆ is negative, the test i != nyear is never false, because i starts at 1 and increases with every

step. The program dies in an infinite loop.

The remedy is simple. Test

◆ for (i = 0; i < nyear; i++) . . .

6.3 do Loops 235

◆ For floating-point values, there is another reason not to use !=: Because of roundoff errors,
◆ the exact termination point may never be reached.
◆ Of course, you would never write

◆ for (rate = 5; rate != 10; rate = rate + 0.3333333) . . .

◆ because it is highly unlikely that rate would match 10 exactly after 15 steps. But the same
◆ problem may happen for the harmless-looking
◆ for (rate = 5; rate != 10; rate = rate + 0.1) . . .

◆ The number 0.1 is exactly representable in the decimal system, but the computer represents
◆ floating-point numbers in binary. There is a slight error in any finite binary representation of
◆ 1/10, just as there is a slight error in a decimal representation 0.3333333 of 1/3. Maybe rate is
◆ exactly 10 after 50 steps; maybe it is off by a tiny amount. There is no point in taking chances.
◆ Just use < instead of !=:

◆ for (rate = 5; rate < 10; rate = rate + 0.1) . . .

Random Fact 6.1


◆ Spaghetti Code

◆ In this chapter we used flowcharts to illustrate the behavior of the loop statements. It used to
◆ be common to draw flowcharts for every method, on the theory that flowcharts are easier to
◆ read and write than the actual code. Nowadays, flowcharts are no longer routinely used for
◆ program development and documentation.
◆ Flowcharts have one fatal flaw. Although it is possible to express the while, for, and
◆ do loops with flowcharts, it is also possible to draw flowcharts that cannot be programmed
◆ with loops. Consider the chart in Figure 4. The top of the flowchart is simply a statement

◆ year = 1;

◆ The lower part is a do loop:
◆ do
◆ { year++;
◆ double interest = balance * rate / 100;

balance = balance + interest;

} while (balance < 2 * initialBalance);

◆ But how can you join these two parts? According to the flowchart, you are supposed to jump
◆ from the first statement into the middle of the loop, skipping the first statement.

◆ year = 1;
◆ goto a; // not an actual Java statement
◆ do
◆ { year++;
◆ a:

236 Chapter 6. Iteration


◆ year = 1





◆ year++



◆ Add interest




◆ balance < 2 * Yes
◆ initialBalance?



◆ No
◆ Print year


◆ Figure 4


◆ Spaghetti Code


◆ double interest = balance * rate / 100;
◆ balance = balance + interest;
◆ } while (balance < 2 * initialBalance);
◆ In fact, why even bother with the do loop? Here is a faithful interpretation of the flowchart:

◆ year = 1;
◆ goto a; // not an actual Java statement
◆ b:
◆ year++;
◆ a:
◆ double interest = balance * rate / 100;
◆ balance = balance + interest;
◆ if (balance < 2 * initialBalance) goto b;

◆ This nonlinear control flow turns out to be extremely hard to read and understand if you have
◆ more than one or two goto statements. Because the lines denoting the goto statements weave
◆ back and forth in complex flowcharts, the resulting code is named spaghetti code.
◆ In 1968 the influential computer scientist Edsger Dijkstra wrote a famous note, entitled
◆ “Goto statements considered harmful” [2], in which he argued for the use of loops instead of
◆ unstructured jumps. Initially, many programmers who had been using goto for years were
◆ mortally insulted and dug out examples where the use of goto does lead to clearer or faster
6.4 Nested Loops 237

◆ code. Some languages offer weaker forms of goto that are less harmful, such as the break
◆ statement in Java, discussed in Advanced Topic 6.3. Nowadays, most computer scientists ac-
◆ cept Dijkstra’s argument and fight bigger battles than optimal loop design.

6.4 Nested Loops

Let us print a table that lists the values of the powers x y for all x between 1 and 10
and y between 1 and 8:
1 1 1 1 1 1 1 1
2 4 8 16 32 64 128 256
3 9 27 81 243 729 2187 6561
4 16 64 256 1024 4096 16384 65536
5 25 125 625 3125 15625 78125 390625
6 36 216 1296 7776 46656 279936 1679616
7 49 343 2401 16807 117649 823543 5764801
8 64 512 4096 32768 262144 2097152 16777216
9 81 729 6561 59049 531441 4782969 43046721
10 100 1000 10000 100000 1000000 10000000 100000000
The basic idea is simple. You have to print ten rows:
for (int x = 1; x <= 10; x++)
{ // print table row
. . .
}
How do you print a table row? You need to print the values x 1 , x 2 , . . . , x 8 . You need
to program another loop:
for (int y = 1; y <= 8; y++)
{ int p = (int)Math.pow(x, y);
System.out.print(p + " ");
}
System.out.println();
This loop prints a table row, including the newline at the end. Putting both loops
together yields two nested loops:
for (int x = 1; x <= 10; x++)
{ // print table row

for (int y = 1; y <= 8; y++)


{ int p = (int)Math.pow(x, y);
System.out.print(p + " ");
}
System.out.println();
}
The pair of loops prints the following table:
238 Chapter 6. Iteration

1 1 1 1 1 1 1 1
2 4 8 16 32 64 128 256
3 9 27 81 243 729 2187 6561
4 16 64 256 1024 4096 16384 65536
5 25 125 625 3125 15625 78125 390625
6 36 216 1296 7776 46656 279936 1679616
7 49 343 2401 16807 117649 823543 5764801
8 64 512 4096 32768 262144 2097152 16777216
9 81 729 6561 59049 531441 4782969 43046721
10 100 1000 10000 100000 1000000 10000000 100000000
Ugh, this looks terrible. The columns don’t line up. This is a common problem when
printing columns of numbers. To solve it, convert each value into a string and keep
adding spaces until the string has the desired length. Then print the padded string.
String pstr = "" + p;

// pad with spaces

while (pstr.length() < COLUMN_WIDTH)


pstr = " " + pstr;

System.out.print(pstr);
Here is the complete program:

Program Table.java
public class Table
{ public static void main(String[] args)
{ final int COLUMN_WIDTH = 10;

for (int x = 1; x <= 10; x++)


{ // print table row

for (int y = 1; y <= 8; y++)


{ int p = (int)Math.pow(x, y);

// convert value to string

String pstr = "" + p;


// pad with spaces

while (pstr.length() < COLUMN_WIDTH)


pstr = " " + pstr;

System.out.print(pstr);
}
System.out.println();
}
}
}
6.5 Processing Input 239

Now you have a total of three nested loops! The outer for loop executes 10 times.
In each iteration, the inner for loop runs 8 times. Therefore, a total of 80 values are
computed. For each of these values, the while loop is executed to pad the result
string.

6.5 Processing Input

6.5.1 Reading a Set of Numbers


Suppose you want to process a set of numbers, for example a set of measurements, to
compute some property of the data set such as the average or the maximum value.
Let us assume that the numbers are input a line at a time. When reading input in
this form, the challenge is to detect the end of the input. To solve this problem,
you can use the readLine method of the ConsoleReader class (or the standard
BufferedReader class). This method returns one line of input, or a null string at
the end of input.
This loop reads through input data:
boolean done = false;
while (!done)
{ String line = console.readLine();
if (line == null)
done = true;
else
{ process data
}
}
This loop is a little different from the ones you saw before, because the test condition
is a variable done. That variable stays false until you reach the end of the input data;
then it is set to true. The next time the loop starts at the top, done is true, and the
loop exits.
There is a reason for using a variable. The test for loop termination, line ==
null, occurs in the middle of the loop, not at the top or the bottom. You must first
try to read a number before you know whether the test succeeds. In Java, there isn’t
a ready-made control structure for the pattern “do work, then test, then do more
work”. Therefore, use a combination of a while loop and a boolean variable. This
pattern is sometimes called “loop and a half”. Some programmers find it clumsy to
introduce a control variable for such a loop. Advanced Topic 6.2 shows a number of
alternatives.
Let us put this loop to work to write a simple program that computes the average
of a set of measurements. You should read the input values as strings, not as numbers.
The reason is that the readLine method returns a null string at the end of input,
allowing us to detect when the end of the data set has been reached. (Note that a
null string is not the same as an empty string "". If the user enters a blank line, then
readLine returns an empty string. )
240 Chapter 6. Iteration

You read the input as a string, but you want to interpret it as a number. Therefore,
you must use the Integer.parseInt or Double.parseDouble method to convert
the input data from a string to a number. Here is the program to compute the average
of a set of input data.

Program Average.java
public class Average
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);
System.out.println("Enter data.");

double sum = 0;
int count = 0;

// compute sum of all input values

boolean done = false;


while (!done)
{ String inputLine = console.readLine();
if (inputLine == null)
done = true;
else
{ double x = Double.parseDouble(inputLine);
sum = sum + x;
count++;
}
}
// compute average

if (count == 0)
System.out.println("No data");
else
System.out.println("Average = " + sum / count);
}
}
To supply data to the program, you type the input data, a line at a time. When you
are done typing, you must indicate to the operating system that all console input for
this program has been supplied. The mechanism for this differs from one operating
system to the other. For example, in DOS you type Ctrl+Z, and on UNIX, you type
Ctrl+D, to indicate the end of console input. This special keystroke combination is a
signal to the operating system (and not to Java) to end console input. The System.in
stream never sees this special character. Instead, it just senses the end of the input.
The readLine method returns a null string (and not a string containing a control
character) at the end of input.
Of course, typing a long set of numbers at the console is tedious and error-prone.
Productivity hint 6.1 shows you how you can prepare the input data in a file and
6.5 Processing Input 241

use input redirection to have the System.in stream read the characters from that
file. In that case you do not terminate the file with a control character, because the
operating system knows the size of the file and therefore knows where it ends.
But the operating system, not being clairvoyant, cannot know the end of keyboard
input—hence the need for the special control character. (Some ancient versions of
DOS did terminate text files on disk with a Ctrl+Z, and you will still find some “ex-
perts” who tell you that all files have a special end-of-file character at the end. This is
plainly not the case—the operating system uses the size of a disk file to determine its
end.)

Advanced Topic 6.2


◆ The ”Loop and a Half” Problem

◆ When reading data from input, we always used to use a loop like the following, which is
◆ somewhat unsightly:

◆ boolean done = false;
◆ while (!done)
◆ { String inputLine = console.readLine();
◆ if (inputLine == null)
◆ done = true;
◆ else
◆ { process data
◆ }
◆ }

◆ The true test for loop termination is in the middle of the loop, not at the top. This is called a
◆ “loop and a half,” because one must go halfway into the loop before knowing whether one
◆ needs to terminate.
◆ Some programmers very much dislike the introduction of an additional Boolean variable for
◆ loop control. Two Java language features can be used to alleviate the “loop and a half” problem.
◆ I don’t think either is a superior solution, but since both approaches are fairly common, it is
◆ worth knowing about them when reading other people’s code.
◆ You can combine an assignment and a test in the loop condition:

◆ while ((inputLine = console.readLine()) != null)
◆ { process data
◆ }

◆ The expression (inputLine = console.readLine()) != null means, “First read a line;
◆ then test whether the end of the input has been reached.” This is an expression with a side
◆ effect. The primary purpose of the expression is to serve as a test for the while loop, but it also
◆ actually does some work—namely, reading the input and storing it in the variable inputLine.
◆ In general, it is always a bad idea to use side effects, because they make a program hard to
◆ read and maintain. In this case, however, it is somewhat seductive, because it eliminates the
◆ control variable done, which also makes the code hard to read and maintain.
242 Chapter 6. Iteration

◆ The other solution is to exit the loop from the middle, either by a return statement or by
◆ a break statement (see Advanced Topic 6.3). Here is an example. This loop reads input data,
◆ and the method containing the loop returns value when the end of input is encountered.

◆ double sum = 0;
◆ int count = 0;
◆ while (true)
◆ { String inputLine = console.readLine();
◆ if (inputLine == null) // leave loop in the middle
◆ { if (count == 0)
◆ System.out.println("No data");
◆ else
◆ System.out.println("Average = " + sum / count);
◆ return;
◆ }
◆ double x = Double.parseDouble(inputLine);
◆ sum = sum + x;
◆ count++;
◆ }

Advanced Topic 6.3


◆ The break and continue Statements

◆ You already encountered the break statement in Advanced Topic 5.2, where it was used to
◆ exit a switch statement. In addition to breaking out of a switch statement, a break statement
◆ can also be used to exit a while, for, or do loop. For example, the break statement in the
◆ following loop terminates the loop when the end of input is reached.

◆ while (true)
◆ { String inputLine = console.readLine();
◆ if (inputLine == null) // leave loop in the middle
◆ break;
◆ double x = Double.parseDouble(inputLine);
◆ sum = sum + x;
◆ count++;
◆ }

◆ In general, a break is a very poor way of exiting a loop. Misuse of a break caused the failure
◆ of an AT&T 4ESS telephone switch on January 15, 1990. The failure propagated through the
◆ entire U.S. network, rendering it nearly unusable for about nine hours. A programmer had
◆ used a break to terminate an if statement. Unfortunately, break cannot be used with if, so
◆ the program execution broke out of the enclosing switch statement, skipping some variable
◆ initializations and running into chaos [3, p. 38]. Using break statements also makes it difficult
◆ to use correctness proof techniques (see Advanced Topic 6.6).
◆ However, when faced with the bother of introducing a separate loop control variable, some
◆ programmers find that break statements are beneficial in the “loop and a half” case. This issue
6.5 Processing Input 243

◆ is often the topic of heated (and quite unproductive) debate. In this book, we won’t use the
◆ break statement, and we leave it to you if you like to use it in your own programs.
◆ In Java, there is a second form of the break statement that is used to break out of a nested
◆ statement. The statement break label ; immediately jumps to the end of the statement that
◆ is tagged with a label. Any statement (including if and block statements) can be tagged with
◆ a label—the syntax is

◆ label : statement

The labeled break statement was invented to break out of a set of nested loops.

◆ outerloop:
◆ while (outer loop condition)
◆ { . . .
◆ while (inner loop condition)
◆ { . . .
◆ if (something really bad happened)
◆ break outerloop;

}

}


jumps here if something really bad happened
◆ Naturally, this situation is quite rare.
◆ Finally, there is another goto-like statement, the continue statement, which jumps to the
◆ end of the current iteration of the loop. Here is a possible use for this statement:

◆ double sum = 0;
◆ int count = 0;
◆ String inputLine;
◆ do
◆ { inputLine = console.readLine();
◆ if (inputLine == null) continue; // jump to the end of the loop body
◆ double x = Double.parseDouble(inputLine);
◆ sum = sum + x;
◆ count++;
◆ // continue statement jumps here
◆ } while (inputLine != null);

◆ By using the continue statement, you don’t need to place the remainder of the loop code
◆ inside an else clause. This is a minor benefit. Few programmers use this statement.

6.5.2 Detecting a Sentinel


Suppose you need to read in two sets of data from a file. You can’t just use the end of
the file as the termination criterion. There must be some indication where the first
data set ends and the second one begins.
Sometimes you are lucky and no input value can be zero. Then you can prompt
the user to keep entering numbers, or 0 to finish that data set. If zero is allowed
244 Chapter 6. Iteration

but negative numbers are not, you can use ⫺1 to indicate termination. Such a
value, which is not an actual input but serves as a signal for termination, is called a
sentinel.
The following program reads in data with a zero value as a sentinel. After
reading the input data, this program could continue to read and process a second
data set.

Program Sentinel1.java
public class Sentinel1
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);
System.out.println("Enter data (0 to finish):");

double sum = 0;
int count = 0;

// compute sum of all input values

boolean done = false;


while (!done)
{ String inputLine = console.readLine();
double x = Double.parseDouble(inputLine);
if (x == 0)
done = true;
else
{ sum = sum + x;
count++;
}
}

// compute average

if (count == 0)
System.out.println("No data");
else
System.out.println("Average = " + sum / count);
}
}
Using a special number as a sentinel works if there is some restriction on the input. In
many cases, though, there isn’t. Suppose you want to compute the average of a data
set that may contain 0 or negative values. You can’t use ⫺13 as a sentinel, even if it is
your lucky number. That number just might occur as a legitimate value in some data
set, and you wouldn’t be so lucky. In that case, you might prompt the user ”Enter
a number, Q to finish”. But now you must be careful reading the input. If the "Q" is
encountered in a call to readDouble, then an exception is thrown, and the program
terminates. (Alternatively, with the improved behavior described in Advanced Topic
6.4, the program keeps prompting for a number.) Instead, simply read in the input as
a string and then convert it to a number if it is not "Q".
6.5 Processing Input 245

Program Sentinel2.java
public class Sentinel2
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);
System.out.println("Enter data (Q to finish):");

double sum = 0;
int count = 0;

// compute sum of all input values

boolean done = false;


while (!done)
{ String inputLine = console.readLine();
if (inputLine.equalsIgnoreCase("Q"))
done = true;
else
{ double x = Double.parseDouble(inputLine);
sum = sum + x;
count++;
}
}
// compute average

if (count == 0)
System.out.println("No data");
else
System.out.println("Average = " + sum / count);
}
}

Advanced Topic 6.4


◆ Coping with Number Format Exceptions

◆ The methods Integer.parseInt and Double.parseDouble throw a NumberFormat-
◆ Exception if you supply a string that isn’t a number. Until now, you have had to live with
◆ this fact, and simply had to make sure that the users of your programs (mainly yourself
◆ and your instructor or grader) cooperated and supplied properly formatted inputs. If an im-
◆ properly formatted input was supplied, the program terminated, because no handler for the
◆ NumberFormatException was provided.
◆ Of course, a robust program should not simply terminate. Instead, it would be preferable
◆ to keep prompting the user for the proper input. To do this properly, you need to know about
◆ exception handling, a topic that we will cover in detail in Chapter 13. However, if you studied
◆ the implementation of the ConsoleReader class in Chapter 3, you already saw how to catch
◆ an IOException. To handle a formatting error, you need to catch a NumberFormatException
◆ instead. The following loop keeps reading an integer until the user has typed a properly
246 Chapter 6. Iteration

◆ formatted number. (We use the trim method to remove leading or trailing white space from
◆ the input string. For example, if the user enters " 3", then the Integer.parseInt method
◆ complains that the leading space is not a legal part of a number. The trim method returns a
◆ string with that space removed.)

◆ int n = 0;
◆ boolean inputOk = false;
◆ do
◆ { try
◆ { String inputString = console.readLine();
◆ n = Integer.parseInt(inputString.trim());
◆ inputOk = true;
◆ }
◆ catch(NumberFormatException e)
◆ { System.out.println("Input error. Try again.");
◆ }
◆ } while (!inputOk);

◆ If the call to parseInt succeeds, then the assignment inputOk = true is carried out, and
◆ the loop exits. Otherwise, the catch clause for the NumberFormatException is executed. An
◆ error message is printed, and the loop is repeated because inputOk is still false.

6.5.3 String Tokenization


In the last examples, input data were provided a line at a time. However, sometimes
it is convenient if an input line can contain several items of input data. Suppose an
input line contains two numbers:
5.5 10000
You can’t convert the string "5.5 10000" to a number, because the parse method
would complain that this string is not a legal number and throw an exception. In-
stead, you need to break the input line into a sequence of strings, each of which
represents a separate input item. There is a special class, the StringTokenizer, that
can break up a string into items or, as they are sometimes called, tokens. By default,
the string tokenizer uses white space (spaces, tabs, and newlines) as delimiters. For
example, the string "5.5 10000" will be decomposed into two tokens: "5.5" and
"10000". The delimiting white space is discarded.
Here is how you break up a string. Construct a StringTokenizer object and
supply the string to be broken up in the constructor:
StringTokenizer tokenizer = new StringTokenizer(inputLine);
Then keep calling the nextToken method to get the next token.
However, if the entire string has been consumed and there are no more tokens, the
nextToken method is a bit hostile and throws an exception rather than returning a
null string. Therefore, you need to call the hasMoreTokens method to ensure that
there still are tokens to be processed. The following loop traverses all tokens in a
string:
6.5 Processing Input 247

while (tokenizer.hasMoreTokens())
{ String token = tokenizer.nextToken();
process token
}

Alternatively, you can use the countTokens method to find out how many tokens
are in the string. Then you get the tokens as follows:
int tokenCount = tokenizer.countTokens();
for (int i = 0; i < tokenCount; i++)
{ String token = tokenizer.nextToken();
process token
}

By the way, note that it would not be a good idea to write the seemingly simpler
loop
for (int i = 0; i < tokenizer.countTokens(); i++) // Inefficient
{ String token = tokenizer.nextToken();
process token
}

This loop would call the countTokens method at the end of every loop iteration.
That is not a good idea, because countTokens is a relatively slow method—it needs
to process the entire input string to determine the number of tokens.
Here is a program that simply counts the number of words in an input file. This
program is useful if you are a writer who is paid by the word. Note that the loop
that breaks up each input line is nested inside the loop that reads the lines.

Program Words.java
import java.util.StringTokenizer;

public class Words


{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);
System.out.println("Enter Words:");

int count = 0;

boolean done = false;


while (!done)
{ String inputLine = console.readLine();
if (inputLine == null)
done = true;
else
{ // break up input line into words

StringTokenizer tokenizer
= new StringTokenizer(inputLine);
248 Chapter 6. Iteration

while (tokenizer.hasMoreTokens())
{ tokenizer.nextToken(); // read and discard
count++; // count each word
}
}
}

System.out.println(count + " words");


}
}

Productivity Hint 6.1


◆ Redirection of Input and Output

◆ Consider the word-counting program of Section 6.3.2. How would you use it? You
◆ would type text in, and at the end of the input the program would tell you how many
◆ words you typed. However, none of the words would be saved for posterity. That is truly
◆ dumb—you would never want to use such a program. Such programs are not intended for key-
◆ board input.
◆ The program does make a lot of sense if input is read from a file. The command line inter-
◆ faces of most operating systems provide a way to link a file to the input of a program, as if all
◆ the characters in the file had actually been typed by a user. If you type
◆ java Words < article.txt

◆ the word-counting program is executed. Its input instructions no longer expect input from the
◆ keyboard. The readLine method gets the input from the file article.txt.
◆ This mechanism works for any program that reads its input from the standard input stream
◆ System.in. By default, the standard input is tied to the keyboard, but it can be tied to any file
◆ by specifying input redirection on the command line.
◆ If you have always launched your program from the integrated environment, you need
◆ to find out whether your environment supports input redirection. If it does not, you need to
◆ learn how to open a command window (often called a shell) and launch the program in the
◆ command window by typing its name and redirection instructions.
◆ You can also redirect output. In this program, that is not terribly useful. If you run

java Words < article.txt > output.txt

◆ the file output.txt contains two lines (“Enter words:” and something like “513 words”). How-
◆ ever, redirecting output is obviously useful for programs that produce lots of it. You can print
◆ the file containing the output or edit it before you turn it in for grading.

Advanced Topic 6.5


◆ Pipes
◆ Output of one program can become the input of another program. Here is a simple program
◆ that writes each word of the input file onto a separate line:
6.5 Processing Input 249

Program Split.java

◆ import java.util.StringTokenizer;


◆ public class Split
◆ { public static void main(String[] args)
◆ { ConsoleReader console = new ConsoleReader(System.in);

◆ boolean done = false;
◆ while (!done)
◆ { String inputLine = console.readLine();
◆ if (inputLine == null)
◆ done = true;

else

{ // break input line into words


◆ StringTokenizer tokenizer
◆ = new StringTokenizer(inputLine);
◆ while (tokenizer.hasMoreTokens())
◆ { // print each word
◆ String word = tokenizer.nextToken();
◆ System.out.println(word);
◆ }
◆ }
◆ }
◆ }

}

◆ Then

◆ java Split < article.txt


◆ lists the words in the file article.txt, one on each line. That isn’t too exciting, but it becomes
◆ useful when combined with another program: sort. You don’t yet know how to write a pro-
◆ gram that sorts strings, but most operating systems have a sort program. A sorted list of the
◆ words in a file would be quite useful—for example, for making an index.
◆ You can save the unsorted words in a temporary file:

◆ java Split < article.txt > temp.txt
◆ sort < temp.txt > sorted.txt

◆ Now the sorted words are in the file sorted.txt.
◆ Because this operation is so common, there is a command line shorthand for it.

◆ java Split < article.txt | sort > sorted.txt

◆ The split program runs first, reading input from article.txt. Its output becomes the input of
◆ the sort program. The output of sort is saved in the file sorted.txt. The | operator instructs
◆ the operating system to construct a pipe linking the output of the first program to the input of
◆ the second.
250 Chapter 6. Iteration

◆ Figure 5 article.txt java Split sort java Unique sorted.txt




◆ A Series of
◆ Pipes



◆ The file sorted.txt has one blemish. It is likely to contain runs of repeated words, like

◆ a
◆ a
◆ a
◆ an
◆ an
◆ anteater
◆ asia

◆ This is easy to fix with another program that removes adjacent duplicates. Removing duplicates
◆ in arbitrary positions is quite hard, but adjacent duplicates are easy to handle:


◆ Program Unique.java

◆ public class Unique
◆ { public static void main(String[] args)
◆ { ConsoleReader console = new ConsoleReader(System.in);

◆ String lastLine = "";

◆ boolean done = false;

◆ while(!done)
◆ { String inputLine = console.readLine();
◆ if (inputLine == null)
◆ done = true;
◆ else if (!inputLine.equals(lastLine))
◆ { // it’s a different line from its predecessor
◆ System.out.println(inputLine);
◆ lastLine = inputLine;

}

}

◆ }
◆ }
◆ The sorted word list, with duplicates removed, is obtained as the series of pipes (see Figure 5).

◆ java Split < article.txt | sort | java Unique > sorted.txt

◆ Redirection and pipes make it possible to combine simple programs to do useful work.
◆ This approach was pioneered in the UNIX operating system, which comes with dozens of
◆ commands that perform common tasks and are designed to be combined with each other.
6.5 Processing Input 251

6.5.4 Traversing the Characters


in a String
In the last section, you learned how to decompose a string into tokens. Sometimes,
you need to go further and analyze the individual characters of a string. The charAt
method of the String class returns an individual character as a value of the char type:
s.charAt(i) is the character at position i of the string s. As with the substring
method, the positions in a string are counted starting from 0 . That is, the parameter
i in the call s.charAt(i) must be a value between 0 and s.length() - 1.
Therefore, the general pattern to traverse all characters in a string is
for (int i = 0; i < s.length(); i++)
{ char ch = s.charAt(i);
do something with ch
}

This is the first time you have encountered the char type. A variable of type char
can hold a single character. There isn’t a lot you can do with a single character. You
can concatenate it with a string, such as

String s;
String t = s + ch;

You can compare it against a character constant. Character constants look like string
constants, except that character constants are delimited by single quotes: 'A' is a
character, "A" is a string containing a single character. You can use escape sequences
(see Advanced Topic 1.1) inside character constants. For example, '\n' is the newline
character, and '\u00E9' is the character é.
Let us use the string traversal pattern to write a method that computes the re-
verse of a string. For example, the reverse of "Hello" is "olleH". To compute the
reverse, stick each new character before the other characters that you already found.
For example, the reverse of "Hello" is built up as
""
"H"
"eH"
"leH"
"lleH"
"olleH"

Program Reverse.java
public class Reverse
{ public static void main(String[] args)
{ ConsoleReader console = new ConsoleReader(System.in);
System.out.println("Please enter a string:");
String s = console.readLine();
String r = "";
for (int i = 0; i < s.length(); i++)
252 Chapter 6. Iteration

{ char ch = s.charAt(i);
r = ch + r; // add ch in front
}
System.out.println(s + " reversed is " + r);
}
}

Quality Tip 6.3


◆ Symmetric and Asymmetric Bounds

◆ It is easy to write a loop with i going from 1 to n:
◆ for (i = 1; i <= n; i++) . . .

◆ The values for i are bounded by the relation 1 ⱕ i ⱕ n. Because there are ⱕ comparisons
◆ on both bounds, the bounds are called symmetric.
◆ When traversing the characters in a string, the bounds are asymmetric.
◆ for (i = 0; i < s.length(); i++) . . .

◆ The values for i are bounded by 0 ⱕ i ⬍ s.length(), with a ⱕ comparison to the left and
◆ a ⬍ comparison to the right. That is appropriate, because s.length() is not a valid position.
◆ It is not a good idea to force symmetry artificially:
◆ for (i = 0; i <= s.length() - 1; i++) . . .

◆ That is more difficult to read and understand.
◆ For every loop, consider which form is most natural according to the needs of the problem
◆ and use that.

Quality Tip 6.4


◆ Count Iterations

Finding the correct lower and upper bounds for an iteration can be confusing. Should I start at

◆ 0? Should I use <= b or < b as a termination condition?
◆ Counting the number of iterations is a very useful device for better understanding a loop.
◆ Counting is easier for loops with asymmetric bounds. The loop
◆ for (i = a; i < b; i++) . . .

◆ is executed b - a times. For example, the loop traversing the characters in a string,
◆ for (i = 0; i < s.length(); i++) . . .

◆ runs s.length() times. That makes perfect sense, since there are s.length() characters in
◆ a string.
◆ The loop with symmetric bounds,

◆ for (i = a; i <= b; i++)
◆ is executed b ⫺ a + 1 times. That “Ⳮ1” is the source of many programming errors. For example,
6.6 Random Numbers and Simulations 253

◆ for (x = 0; x <= 10; x++)



runs 11 times. Maybe that is what you want; if not, start at 1 or use < 10.

One way to visualize this “+1” error is to think of the posts and sections of a fence. Suppose

the fence has ten sections (=). How many posts (|) does it have?

◆ |=|=|=|=|=|=|=|=|=|=|

◆ A fence with ten sections has eleven posts. Each section has one post to the left, and there is
◆ one more post after the last section. Forgetting to count the last iteration of a “<=” loop is often
◆ called a “fence post error”.
◆ If the increment is a value c other than 1, then the counts are
◆ (b ⫺ a)/c for the asymmetric loop

(b ⫺ a)/c Ⳮ 1 for the symmetric loop

◆ For example, the loop for (i = 10; i <= 40; i += 5) executes (40⫺10)/5Ⳮ1 ⳱ 7 times.

6.6 Random Numbers


and Simulations

In a simulation you generate random events and evaluate their outcomes. Here is a
typical problem that can be decided by running a simulation: the Buffon needle ex-
periment, devised by Comte Georges-Louis Leclerc de Buffon (1707–1788), a French
naturalist. A needle of length 1 inch is dropped onto paper that is ruled with lines 2
inches apart. If the needle drops onto a line, count it as a hit. Buffon conjectured that
the quotient tries/hits approximates ␲ . (See Figure 6.)
Now, how can you run this experiment in the computer? You don’t actually want
to build a robot that drops needles on paper. The Random class of the Java library
implements a random number generator, which produces numbers that appear to be
completely random. To generate random numbers, you construct an object of the
Random class, and then apply one of the following methods:

nextInt(n) returns a random integer between


the integers 0 (inclusive) and n (exclusive)
nextDouble() returns a random floating-point number
between 0 (inclusive) and 1 (exclusive)

Figure 6

The Buffon Needle


Experiment
254 Chapter 6. Iteration

For example, you can simulate the throwing of a die as follows:


Random generator = new Random();
int d = 1 + generator.nextInt(6);
The call generator.nextInt(6) gives you a random number between 0 and 5 (in-
clusive). Add 1 to obtain a number between 1 and 6.
To give you a feeling for the random numbers, run the following program a few
times:

Program Dice.java
import java.util.Random;

public class Dice


{ public static void main(String[] args)
{ Random generator = new Random();
// roll dice ten times

for (int i = 1; i <= 10; i++)


{ int d = 1 + generator.nextInt(6);
System.out.print(d + " ");
}
System.out.println();
}
}
Here are a few typical program runs.
6 5 6 3 2 6 3 4 4 1
3 2 2 1 6 5 3 4 1 2
4 1 3 2 6 2 4 3 3 5
As you can see, this program produces a different stream of simulated dice throws
every time it is run. Actually, the numbers are not completely random. They are
drawn from very long sequences of numbers that don’t repeat for a long time. These
sequences are actually computed from fairly simple formulas; they just behave like
random numbers. For that reason, they are often called pseudorandom numbers. How to
generate good sequences of numbers that behave like truly random sequences is an im-
portant and well-studied problem in computer science. We won’t investigate this is-
sue further, though; we’ll just use the random numbers produced by the Random class.
To run the Buffon needle experiment, we have to work a little harder. When you
throw a die, it has to come up with one of six faces. When throwing a needle, how-
ever, there are many possible outcomes. You must generate two random numbers:
one to describe the starting position and one to describe the angle of the needle with
the x-axis. Then you need to test whether the needle touches a grid line. Stop after
10,000 tries.
Let us agree to generate the lower point of the needle. Its x-coordinate is irrelevant,
and you may assume its y-coordinate ylow to be any random number between 0 and
2. However, since it can be a random floating-point number, we use the nextDouble
method of the Random class. It returns a random floating-point number between 0
and 1. Multiply by 2 to get a random number between 0 and 2.
6.6 Random Numbers and Simulations 255

Figure 7 yhigh
2

Variables in a Trial of
the Buffon Needle
Experiment ylow α

The angle ␣ between the needle and the x-axis can be any value between 0 degrees
and 180 degrees. The upper end of the needle has y-coordinate

yhigh ⳱ ylow Ⳮ sin ␣

The needle is a hit if yhigh is at least 2. See Figure 7.


Here is the program to carry out the simulation of the needle experiment.

Program Buffon.java
import java.util.Random;

public class Buffon


{ public static void main(String[] args)
{ Random generator = new Random();
int hits = 0;
final int NTRIES = 10000;

for (int i = 1; i <= NTRIES; i++)


{ // simulate needle throw

double ylow = 2 * generator.nextDouble();


double angle = 180 * generator.nextDouble();

// compute high point of needle

double yhigh = ylow + Math.sin(Math.toRadians(angle));


if (yhigh >= 2) hits++;
}

// print approximation of PI

System.out.println("Tries / Hits = "


+ (NTRIES * 1.0) / hits);
}
}
256 Chapter 6. Iteration

On one computer I obtained the result 3.10 when running 10,000 iterations and
3.1429 when running 100,000 iterations.
The point of this program is not to compute ␲ —there are far more efficient ways
for that purpose. Rather, the point is to show how a physical experiment can be
simulated on the computer. Buffon had to drop the needle physically thousands of
times and record the results, which must have been a rather dull activity. You can
have the computer execute the experiment quickly and accurately.
Simulations are very common computer applications. All simulations use essen-
tially the same pattern as the code of this example: In a loop, a large number of
sample values are generated; the values of certain observations are recorded for each
sample; when the simulation is completed, the averages of the observed values are
printed out.
A typical example of a simulation is the modeling of customer queues at a bank
or a supermarket. Rather than observing real customers, one simulates their arrival
and their transactions at the teller window or checkout stand in the computer. One
can try out different staffing or building layout patterns in the computer simply by
making changes in the program. In the real world, making many such changes and
measuring their effect would be impossible, or at least very expensive.

Advanced Topic 6.6


◆ Loop Invariants

◆ Consider the task of computing a n , where a is a floating-point number and n is a positive
◆ integer. Of course, you can multiply a ⫻ a ⫻ ⭈⭈⭈ ⫻ a, n times, but if n is large, you’ll end
◆ up doing a lot of multiplications. The following loop computes a n in far fewer steps:

◆ double a = . . .;
◆ int n = . . .;
◆ double r = 1;
◆ double b = a;
◆ int i = n;
◆ while (i > 0)
◆ { if (i % 2 == 0) // i is even
◆ { b = b * b;
◆ i = i / 2;
◆ }
◆ else
◆ { r = r * b;
◆ i--;
◆ }
◆ }
◆ // now r equals Math.pow(a, n);

◆ Consider the case n ⳱ 100. The method performs the following steps.
6.6 Random Numbers and Simulations 257


◆ b i r
◆ a 100 1

a2 50

◆ a4 25
◆ 24 a4
8
◆ a 12 a4
◆ a 16
6
◆ a32 3
◆ 2 a36
◆ 64
a 1

0 a100




◆ Amazingly enough, the algorithm yields exactly a100 . Do you understand why? Are you con-
◆ vinced it will work for all values of n? Here is a clever argument to show that the method
◆ always computes the correct result. We will demonstrate that whenever the program reaches

the top of the while loop, it is true that

◆ r ⭈ bi ⳱ an (I)

◆ Certainly, it is true the first time around, because r ⳱ 1, b ⳱ a, and i ⳱ n. Suppose that (I)
◆ holds at the beginning of the loop. We label the values of r, b, and i as “old” when entering
◆ the loop and as “new” when exiting the loop. We assume that upon entry

i
◆ rold ⭈ bold
old
⳱ an

◆ In the loop we have to distinguish two cases: i even and i odd. If i is even, the loop performs
◆ the following transformations:

rnew ⳱ rold

◆ bnew ⳱ b2old

◆ inew ⳱ iold /2
◆ Therefore,

◆ rnew ⭈ binew
new 2⭈i
⳱ rold ⭈ bold old
/2


i
◆ ⳱ rold ⭈ bold
old


◆ ⳱ an

◆ On the other hand, if i is odd, then

◆ rnew ⳱ rold ⭈ bold

◆ bnew ⳱ bold
◆ inew ⳱ iold ⫺ 1

258 Chapter 6. Iteration

◆ Therefore,

◆ i ⫺1
rnew ⭈ binew
new
⳱ rold ⭈ bold + biold
old

◆ i
⳱ rold ⭈ bold
old


◆ ⳱ an

◆ In either case, the new values for r, b, and i fulfill the loop invariant (I). So what? When the
◆ loop finally exits, (I) holds again:

◆ r ⭈ bi ⳱ an

◆ Furthermore, we know that i ⳱ 0, since the loop is terminating. But because i ⳱ 0, r ⭈ bi ⳱
◆ r ⭈ b0 ⳱r. Hence r ⳱ an , and the method really does compute the nth power of a.
◆ This technique is quite useful, because it can explain an algorithm that is not at all obvious.
◆ The condition (I) is called a loop invariant because it is true when the loop is entered, at the
◆ top of each pass, and when the loop is exited. If a loop invariant is chosen skillfully, you may
◆ be able to deduce correctness of a computation. See [4] for another nice example.

Random Fact 6.2


◆ Correctness Proofs

◆ In Advanced Topic 6.6 we introduced the technique of loop invariants. If you skipped that note,
◆ have a glance at it now. That technique can be used to prove rigorously that a loop computes
◆ exactly the value that it is supposed to compute. Such a proof is far more valuable than any
◆ testing. No matter how many test cases you try, you always worry whether another case that
◆ you haven’t tried yet might show a bug. A proof settles the correctness for all possible inputs.
◆ For some time, programmers were very hopeful that proof techniques such as loop invari-
◆ ants would greatly reduce the need of testing. You would prove that each simple method is
◆ correct, and then put the proven components together and prove that they work together as
◆ they should. Once it is proved that main works correctly, no testing is required at all! Some
◆ researchers were so excited about these techniques that they tried to omit the programming
◆ step altogether. The designer would write down the program requirements, using the notation
◆ of formal logic. An automatic prover would prove that such a program could be written and
◆ generate the program as part of its proof.

Unfortunately, in practice these methods never worked very well. The logical notation to

describe program behavior is complex. Even simple scenarios require many formulas. It is easy

enough to express the idea that a method is supposed to compute a n , but the logical formulas

◆ describing all methods in a program that controls an airplane, for instance, would fill many
◆ pages. These formulas are created by humans, and humans make errors when they deal with
◆ difficult and tedious tasks. Experiments showed that instead of buggy programs, programmers
◆ wrote buggy logic specifications and buggy program proofs.
◆ Van der Linden [3], p. 287, gives some examples of complicated proofs that are much harder
◆ to verify than the programs they are trying to prove.
◆ Program proof techniques are valuable for proving the correctness of individual methods
◆ that make computations in nonobvious ways. At this time, though, there is no hope to prove
Chapter Summary 259

◆ any but the most trivial programs correct in such a way that the specification and the proof can
◆ be trusted more than the program. There is hope that correctness proofs will become more
◆ applicable to real-life programming situations in the future. At this point, however, engineer-
◆ ing and management are at least as important as mathematics and logic for the successful
◆ completion of large software projects.

Chapter Summary

1. Loops execute a block of code repeatedly. A termination condition controls how


many times the loop is executed.

2. There are three kinds of loops: while, for, and do loops. You use a for loop
when a number runs from a starting to an ending value with a constant increment or
decrement; do loops are appropriate when the loop body must be executed at least
once.

3. An off-by-one error is a common error when programming loops. You should


count the number of iterations of a for loop and make a choice between symmetric
and asymmetric bounds.

4. Loops can be nested. A typical example for nested loops is printing a table.

5. When reading input, you can detect the end of the input by checking for the end
of the file, or you can use a sentinel value.

6. You can break an input line into words by using a string tokenizer, or you can
access the individual characters.

7. In a simulation, you repeatedly generate random numbers and use them to simu-
late an activity.

8. With simple loops, you can use correctness proofs to show that the loop is correct.
Correctness proofs, as well as informal reasoning about correctness, work best if you
exit from a loop only when the loop condition fails. Therefore, it is best to avoid
breaking out from the middle of a loop.

Further Reading
[1] Alan Feuer, The C Puzzle Book, Prentice-Hall, 1989.
[2] E. W. Dijkstra, “Goto Statements Considered Harmful”, Communications of the ACM, vol.
11, no. 3 (March 1968), pp. 147–148.
260 Chapter 6. Iteration

[3] Peter van der Linden, Expert C Programming, Prentice-Hall, 1994.


[4] Jon Bentley, Programming Pearls, Addison-Wesley, 1986, Chapter 4, “Writing Correct Pro-
grams”.
[5] Kai Lai Chung, Elementary Probability Theory with Stochastic Processes, Undergraduate Texts in
Mathematics, Springer-Verlag, 1974.
[6] Rudolf Flesch, How to Write Plain English, Barnes & Noble Books, 1979.

Classes, Objects, and Methods


Introduced in This Chapter
java.util.Random
nextDouble
nextInt
java.util.StringTokenizer
countTokens
hasMoreTokens
nextToken

Review Exercises

Exercise R6.1. Which loop statements does Java support? Give simple rules when
to use each loop type.

Exercise R6.2. What does the following code print?


for (int i = 0; i < 10; i++)
{ for (int j = 0; j < 10; j++)
System.out.print(i * j % 10);
System.out.println();
}

Exercise R6.3. How often do the following loops execute? Assume that i is not
changed in the loop body.
for (i = 1; i <= 10; i++) . . .
for (i = 0; i < 10; i++) . . .
for (i = 10; i > 0; i--) . . .
for (i = -10; i <= 10; i++) . . .
for (i = 10; i >= 0; i++) . . .
for (i = -10; i <= 10; i = i + 2) . . .
for (i = -10; i <= 10; i = i + 3) . . .
Review Exercises 261

Exercise R6.4. Rewrite the following for loop into a while loop.
int s = 0;
for (int i = 1; i <= 10; i++) s = s + i;

Exercise R6.5. Rewrite the following do loop into a while loop.


int n = 1;
double x = 0;
double s;
do
{ s = 1.0 / (n * n);
x = x + s;
n++;
} while (s > 0.01);

Exercise R6.6. What is an infinite loop? On your computer, how can you terminate
a program that executes an infinite loop?

Exercise R6.7. There are two ways to supply input to System.in. Describe both
methods. Explain how the “end of file” is signaled in both cases.

Exercise R6.8. In DOS/Windows and UNIX, there is no special “end of file” char-
acter stored in a file. Verify that statement by producing a file with known character
count—for example, a file consisting of the following three lines
Hello
cruel
world
Then look at the directory listing. How many characters does the file contain? Re-
member to count the newline characters. (In DOS, you may be surprised that the
count is not what you expect. DOS text files store each newline as a two-character
sequence. The input readers and output streams automatically translate between this
carriage return/line feed sequence used by files and the '\n' character used by Java
programs, so you don’t need to worry about it.) Why does this prove that there is no
“end of file” character? Why do you nevertheless need to type Ctrl+Z/Ctrl+D to end
console input?

Exercise R6.9. How can you read input from System.in (1) a character at a time,
(2) a word at a time, and (3) a line at a time?

Exercise R6.10. Show how to use a string tokenizer to break up the string "Hello,
cruel world!" into tokens. What are the resulting tokens?

Exercise R6.11. Give a strategy for reading input of the form


name of bridge length of bridge
262 Chapter 6. Iteration

Here the name of the bridge can be a single word (“Brooklyn”) or consist of several
words (“Golden Gate”). The length is a floating-point number.

Exercise R6.12. What is a “loop and a half”? Give three strategies to implement the
following “loop and a half”:
loop
{ read employee name
if not OK, exit loop
read employee salary
if not OK, exit loop
give employee 5 percent raise
print employee data
}
Use a Boolean variable, a break statement, and a method with multiple return
statements. Which of these three approaches do you find clearest?

Exercise R6.13. What is a sentinel value? Give simple rules when it is better to use
a sentinel value and when it is better to use the end of the input file to denote the
end of a data sequence. Hint: Consider the number of data sets and the origin of the
data (keyboard input vs. file input).

Exercise R6.14. How would you use a random number generator to simulate the
drawing of a playing card?

Exercise R6.15. What is an “off by one” error? Give an example from your own
programming experience.

Exercise R6.16. Give an example of a for loop in which symmetric bounds are
more natural. Give an example of a for loop in which asymmetric bounds are more
natural.

Exercise R6.17. What are nested loops? Give an example where a nested loop is
typically used.

Programming Exercises

Exercise P6.1. The series of pipes in Advanced Topic 6.5 has one final problem: The
output file contains upper- and lowercase versions of the same word, such as “The”
and “the”. Modify the procedure, either by changing one of the programs or, in the
true spirit of piping, by writing another short program and adding it to the series.

Exercise P6.2. Currency conversion. Write a program that asks the user to enter today’s
exchange rate between U.S. dollars and the Euro. Then the program reads U.S. dollar
Programming Exercises 263

values and converts each to Euro values. Use 0 as a sentinel to denote the end of
inputs.

Exercise P6.3. Write a program that asks the user to enter today’s exchange rate
between U.S. dollars and the Euro. Then the program reads U.S. dollar values and
converts each to Euro values. Use 0 as the sentinel value to denote the end of dollar
inputs. Then the program reads a sequence of Euro amounts and converts them to
dollars. The second sequence is terminated by the end of the input file.

Exercise P6.4. Random walk. Simulate the wandering of an intoxicated person in a


square street grid. Draw a grid of 10 streets horizontally and 10 streets vertically.
Represent the simulated drunkard by a dot, placed in the middle of the grid to start.
For 100 times, have the simulated drunkard randomly pick a direction (east, west,
north, south), move one block in the chosen direction, and redraw the dot. After the
iterations, display the distance that the drunkard has covered. (One might expect
that on average the person might not get anywhere because the moves to different
directions cancel another out in the long run, but in fact it can be shown that with
probability 1 the person eventually moves outside any finite region. See, for example,
[5], chapter 8, for more details.)

Exercise P6.5. Projectile flight. Suppose a cannonball is propelled vertically into the
air with a starting velocity v0 . Any calculus book will tell us that the position of
the ball after t seconds is s(t) ⳱ ⫺0.5 ⭈ g ⭈ t 2 Ⳮ v0 ⭈ t, where g ⳱ 9.81 m/sec2 is
the gravitational force of the earth. No calculus book ever mentions why someone
would want to carry out such an obviously dangerous experiment, so we will do it
in the safety of the computer.
In fact, we will confirm the theorem from calculus by a simulation. In our simula-
tion, we will consider how the ball moves in very short time intervals ⌬t. In a short
time interval the velocity v is nearly constant, and we can compute the distance the
ball moves as ⌬s ⳱ v ⭈ ⌬t. In our program, we will simply set
double deltaT = 0.01;

and update the position by


s = s + v * deltaT;

The velocity changes constantly—in fact, it is reduced by the gravitational force of


the earth. In a short time interval, v ⳱ ⫺g ⭈⌬t, and we must keep the velocity updated
as
v = v - g * deltaT;

In the next iteration the new velocity is used to update the distance.
Now run the simulation until the cannonball falls back onto the earth. Get the ini-
tial velocity as an input (100 m/sec is a good value). Update the position and velocity
100 times per second, but print out the position only every full second. Also print
out the values from the exact formula s(t) ⳱ ⫺0.5 ⭈ g ⭈ t 2 Ⳮ v0 ⭈ t for comparison.
264 Chapter 6. Iteration

What is the benefit of this kind of simulation when an exact formula is avail-
able? Well, the formula from the calculus book is not exact. Actually, the gravita-
tional force diminishes the further the cannonball is away from the surface of the
earth. This complicates the algebra sufficiently that it is not possible to give an exact
formula for the actual motion, but the computer simulation can simply be extended
to apply a variable gravitational force. For cannonballs, the calculus-book formula is
actually good enough, but computers are necessary to compute accurate trajectories
for higher-flying objects such as ballistic missiles.

Exercise P6.6. Most cannonballs are not shot upright but at an angle. If the starting
velocity has magnitude v and the starting angle is ␣ , then the velocity is actually a
vector with components vx ⳱ v cos ␣, vy ⳱ v sin ␣ . In the x-direction the velocity
does not change. In the y-direction the gravitational force takes its toll. Repeat the
simulation from the previous exercise, but store the position of the cannonball as a
Point2D variable. Update the x and y positions separately, and also update the x
and y components of the velocity separately. Every full second, plot the location of
the cannonball on the graphics display. Repeat until the cannonball has reached the
earth again.
This kind of problem is of historical interest. The first computers were designed to
carry out just such ballistic calculations, taking into account the diminishing gravity
for high-flying projectiles and wind speeds.

Exercise P6.7. The Fibonacci sequence is defined by the following rule. The first two
values in the sequence are 1 and 1. Every subsequent value is the sum of the two
values preceding it. For example, the third value is 1 Ⳮ 1 ⳱ 2, the fourth value is
1 Ⳮ 2 ⳱ 3, and the fifth is 2 Ⳮ 3 ⳱ 5. If fn denotes the nth value in the Fibonacci
sequence, then

f1 ⳱ 1
f2 ⳱ 1
fn ⳱ fn⫺1 Ⳮ fn⫺2 if n ⬎ 2

Write a program that prompts the user for n and prints the nth value in the Fibonacci
sequence.
Hint: There is no need to store all values for fn . You only need the last two values
to compute the next one in the series:
fold1 = 1;
fold2 = 1;
fnew = fold1 + fold2;
After that, discard fold2, which is no longer needed, and set fold2 to fold1 and
fold1 to fnew. Repeat computing fnew for an appropriate number of times.

Exercise P6.8. Write a program that prints a bar chart from a data set. The program
should be a graphics applet that prompts the user for the values, all to be entered
Programming Exercises 265

into a single option dialog, separated by spaces (for example, 40 60 50). Assume all
values are between 0 and 100. Then draw a bar chart like this:

Exercise P6.9. Mean and standard deviation. Write a program that reads a set of
floating-point data values from the input. When the end of file is reached, print out
the count of the values, the average, and the standard deviation. The average of a
data set 兵x1 , . . . , xn 其 is x ⳱ 冱 xi /n, where 冱 xi ⳱ x1 Ⳮ ⭈⭈⭈ Ⳮ xn is the sum of the
input values. The standard deviation is

冪 冱(xn ⫺⫺1x)
2
i
S ⳱

However, that formula is not suitable for our task. By the time you have computed
the mean, the individual xi are long gone. Until you know how to save these values,
use the numerically less stable formula

S ⳱ 冪 冱 xi2 ⫺ n1 冱 xi
n⫺1

You can compute this quantity by keeping track of the count, the sum, and the sum
of squares as you process the input values.

Exercise P6.10. Write a graphical applet that prompts a user to enter a number n
and that draws n circles with random center and random radius.

Exercise P6.11. Flesch Readability Index. The following index [6] was invented by
Flesch as a simple tool to gauge the legibility of a document without linguistic
analysis.

◆ Count all words in the file. A word is any sequence of characters delimited by
white space, whether or not it is an actual English word.
◆ Count all syllables in each word. To make this simple, use the following rules:
Each group of adjacent vowels (a,e,i,o,u,y) counts as one syllable (for example,
the “ea” in “real” contributes one syllable, but the “e..a” in “regal” count as two
syllables). However, an “e” at the end of a word doesn’t count as a syllable.
Also, each word has at least one syllable, even if the previous rules give a
count of 0.
266 Chapter 6. Iteration

◆ Count all sentences. A sentence is ended by a period, colon, semicolon, ques-


tion mark, or exclamation mark.
◆ The index is computed by

Index ⳱ 206.835 ⫺ 84.6 ⫻ (Number of syllables / Number of words)


⫺ 1.015 ⫻ (Number of words / Number of sentences)

rounded to the nearest integer.

This index is a number, usually between 0 and 100, indicating how difficult the text
is to read. Some examples for random material for various publications are

Comics 95
Consumer ads 82
Sports Illustrated 65
Time 57
New York Times 39
Auto insurance policy 10
Internal Revenue Code ⫺6

Translated into educational levels, the indices are

91–100 5th grader


81–90 6th grader
71–80 7th grader
66–70 8th grader
61–66 9th grader
51–60 High school student
31–50 College student
0–30 College graduate
Less than 0 Law school graduate

The purpose of the index is to force authors to rewrite their text until the index is
high enough. This is achieved by reducing the length of sentences and by removing
long words. For example, the sentence

The following index was invented by Flesch as a simple tool to estimate the legibility
of a document without linguistic analysis.

can be rewritten as

Flesch invented an index to check whether a text is easy to read. To compute the index,
you need not look at the meaning of the words.

His book [7] contains delightful examples of translating government regulations into
“plain English”.
Your program should read in a text file, compute the legibility index, and print out
the equivalent educational level.
Programming Exercises 267

Exercise P6.12. Factoring of integers. Write a program that asks the user for an integer
and then prints out all its factors. For example, when the user enters 150, the program
should print
2
3
5
5

Exercise P6.13. Prime numbers. Write a program that prompts the user for an integer
and then prints out all prime numbers up to that integer. For example, when the user
enters 20, the program should print
2
3
5
7
11
13
17
19

Recall that a number is a prime number if it is not divisible by any number except 1
and itself.

Exercise P6.14. The best known iterative method for computing the roots of a func-
tion f (that is, the x-values for which f (x) is 0) is Newton–Raphson approximation. To
find the zero of a function whose derivative is also known, compute

xnew ⳱ xold ⫺ f (xold )/f ⬘(xold ).

For this exercise, write a program to compute nth roots of floating-point numbers.
n
Prompt the user for a and n, then obtain 冪 a by computing a zero of the function
f (x) ⳱ x ⫺ a.
n

Exercise P6.15. Write a graphical applet that displays a checkerboard with 64


squares, alternating white and black.

Exercise P6.16. Write a program that reads a series of floating-point numbers and
prints

◆ The maximum value


◆ The minimum value
◆ The average value

Exercise P6.17. The game of Nim. This is a well-known game with a number of
variants. We will consider the following variant, which has an interesting winning
strategy. Two players alternately take marbles from a pile. In each move, a player
268 Chapter 6. Iteration

chooses how many marbles to take. The player must take at least one but at most
half of the marbles. Then the other player takes a turn. The player who takes the last
marble loses.
You will write a program in which the computer plays against a human opponent.
Generate a random integer between 10 and 100 to denote the initial size of the pile.
Generate a random integer between 0 and 1 to decide whether the computer or the
human takes the first turn. Generate a random integer between 0 and 1 to decide
whether the computer plays smart or stupid. In stupid mode, the computer simply
takes a random legal value (between 1 and n/2) from the pile whenever it has a turn.
In smart mode the computer takes off enough marbles to make the size of the pile
a power of two minus 1—that is, 3, 7, 15, 31, or 63. That is always a legal move,
except if the size of the pile is currently one less than a power of two. In that case,
the computer makes a random legal move.
You will note that the computer cannot be beaten in smart mode when it has the
first move, unless the pile size happens to be 15, 31, or 63. Of course, a human player
who has the first turn and knows the winning strategy can win against the computer.

Exercise P6.18. The value of e x can be computed as the power series



xn x2 x3
ex ⳱ 冱 ⳱ 1Ⳮx Ⳮ Ⳮ Ⳮ ⭈⭈⭈
n⳱0
n! 2! 3!

Write a program that computes e x using this formula. Of course, you can’t compute
an infinite sum. Just keep adding values until an individual summand (term) is less
than a certain threshold. At each step, you need to compute the new term and add
it to the total. Update these terms as follows:
term = term * x / n;

Exercise P6.19. Program the following simulation: Darts are thrown at random
points onto the square with corners (1, 1) and (⫺1, ⫺1). If the dart lands inside the
unit circle (that is, the circle with center (0, 0) and radius 1), it is a hit. Otherwise it
is a miss. Run this simulation and use it to determine an approximate value for ␲ .
Explain why this is a better method for estimating ␲ than the Buffon needle program.

Exercise P6.20. It is easy and fun to draw graphs of curves with the Java graphics
library. Simply draw a hundred line segments joining the points (x, f (x)) and (x Ⳮ
d, f (x Ⳮ d )), where x ranges from xmin to xmax and d ⳱ (xmax ⫺ xmin )/100. Draw the
curve f (x) ⳱ x 3 / 100 ⫺ x Ⳮ 10, where x ranges from ⫺10 to 10 in this fashion.

Exercise P6.21. Draw a picture of the “four-leaved rose” whose equation in polar
coordinates is r ⳱ cos 2␪, 0 ⱕ ␪ ⱕ 2␲ . Let ␪ go from 0 to 2␲ in 100 steps. Each
time, compute r and then compute the (x, y) coordinates from the polar coordinates
by using the formula
x ⳱ r cos ␪ , y ⳱ r sin ␪

You can get extra credit if you can vary the number of petals.

You might also like