Tải bản đầy đủ
Chapter 15 – Making things happen. Again and again

Chapter 15 – Making things happen. Again and again

Tải bản đầy đủ

Making things happen. Again and again

203

Conditional statements control the order in which the other statements in
a program are executed. However, even with this level of control the fact
remains that the overall flow of the program is still from the top to the bottom;
there are usually several routes to the end of the program, but the flow is
always in the same prevailing direction. Once a statement has been executed it
can never be executed again. It is not possible to go backwards.
There is a second type of statement that can control the order of execution of
a Java program. Program loops allow statements to be executed a certain number of times, or while some Boolean condition is true, or until some Boolean
condition has become false. This chapter explains the different kinds of loops
that are available and describes how to use loops in your classes and programs.
After reading this chapter you should be able to write methods and programs
that include loops. You should understand the different kinds of loops available in
Java and you should be able to decide which to use to solve a particular problem.

Let’s illustrate this new idea with an example. The previous chapter explained
how to validate the values that users supply to your programs. This is effective
as far as it goes, but the dialogue that has resulted is hardly very friendly. The
end result is that the users are simply told that they have made an error and the
program then finishes; if the user wants to try again with another value (or set
of values) the program has to be executed again and all the values have to be
entered again. This would be especially annoying for a user who had entered
several values correctly only to make a small error with a later value.
In these situations it would be far preferable to have a more complex
dialogue where the user is still informed of the error but is then invited to
re-enter an acceptable value. This dialogue might resemble:
Enter a number between 1 and 10: 20
Error: Number out of range. Try again: 5
Thank you. The number you entered was 5.

204

How to program using Java

This is a much more friendly example of interaction than those that we have
been able to achieve up to now.
To implement a dialogue such as this, one or more statements in a program
must be executed a number of times. Since it is not possible to predict how
many times the user will make a mistake, or even if they will make one at all,
the statements concerned cannot be repeated in the program; rather the same
statements must be executed until the user provides an acceptable value.
This is only one example of where a program loop might be used. There are
many others situations where this is required, but there are in general only three
types of loop:





Part of a program is executed a given number of times. Do this six times.
Part of a program is executed until some condition becomes true. Do this until
the user has entered an acceptable value.
Part of a program is executed while some condition is true. Do this while the
user continues to enter incorrect values.

In each of these examples “part of a program” means one or more statements
enclosed between the usual pair of braces.
The last two possibilities in this list may seem at first sight to be the same
thing, but they are not. There is a subtle difference. A loop that is executed until
some condition becomes true will always be executed at least once, while a
statement that is executed as long as some condition is true may never be
executed at all if the condition is initially false.
Some examples of loops are:





Execute this statement seven times.
Execute this statement, and carry on executing it until the user enters a
negative value at the prompt.
Execute this statement while the user continues to provide valid input values.

These three types of loop are implemented in Java as for loops, do … while
loops and while loops, respectively. The names simply refer to the Java
keywords that are used to express such a loop.

for loops
A for loop causes statements to be executed a certain number of times.
This number might be a known literal value such as 10 or it might be held in a
variable. The first case is used when it is known that the loop will always be
executed the same number of times, while the second allows the number to
be determined as the program is running.
The loop is controlled by a statement of the form:
for (Ͻinitialisation statementϾ; Ͻcontinuation conditionϾ; Ͻchange statementϾ)
The initialisation statement is executed the first time the loop is executed and
the loop continues to execute as long as the continuation condition is true. Each
time the end of the loop is reached the change statement is also executed.
This might all seem a little complicated, so an example illustrates:
for (int i = 0; i < 10; i ++ )

The initialisation statement is often a declaration. In this example, when the
loop is first executed, an integer variable i will be created and initialised to 0.
The loop will execute as long as i is less than 10 (the continuation condition)

Making things happen. Again and again

205

and at the end of each execution 1 is added to i (the change statement). The
practical result of this is that in this case the loop will execute 10 times as
i moves from 0 to 9 inclusive. It is usual to start loops that count in this way
with an initial value of 0.1
The statements controlled by the loop are enclosed in the usual curly
brackets:
for (Ͻinitialisation statementϾ; Ͻcontinuation conditionϾ; Ͻchange statementϾ) {
// statements
}
As usual the statements are indented to emphasise the fact that they are inside
the loop. Any Java statements may appear inside the loop, including conditional statements and other loops.
The variable declared in the initialisation is called the loop’s control variable.
It can be of any data type but it is most usually an integer. It does not necessarily have to be declared inside the first line of the loop, but it most usually is.
The control variable is often used inside the loop. For example, to print the
first 10 integers (starting at 0, of course) the loop would be:
for (int i = 0; i < 10; i ++ ) {
System.out.println (i);
}

A slightly more useful loop prints out the 12 times table:
for (int i = 1; i < 13; i ++ ) {
System.out.print (i);
System.out.print (" x 12 = ");
System.out.println (i * 12);
}

This loop generates a lot of output for a small amount of code. This is the power
of loops. This program might also remind you of Buddy’s “Times Table”
program from Chapter 9; we’ll look at how that program can be rewritten with
a loop later on.
While the control variable can be used in any statement inside the loop there
are two important rules:
1. The control variable should never be altered inside the loop. It should be
altered only when an execution of the statements inside the loop has ended.
2. If the control variable is declared in the header of the loop it cannot be used
once the loop has terminated.
If it is necessary to use the value of the control variable after the loop has
finished it can be declared before the loop:
int i;
for (i = 0; i < 10; i + + ) {
System.out.println (i);
}
// i can be used here

1

Remember that computers start to count at 0, not 1!

206

How to program using Java

Otherwise, attempting to use the control variable after the loop has finished
will result in an error:
for (int i = 0; i < 10; i ++ ) {
System.out.println (i);
}
// using i here is an error

By far the most common use of a for loop is just to execute a set of statements a given number of times; the easiest way of doing this is always to count
up to this number. This gives a loop of a general form that will cause statements
to be executed n times:
for (int i = 0; i < n; i ++ ) {
// statements executed n times
}

Other less common possibilities include printing out just even numbers:
for (int e = 0; e < 10; e += 2) {
System.out.println (e);
}

or counting backwards from a number:
for (int b = 10; b >= 0; b --) {
System.out.println (b);
}

You might want to type these loops into small programs to see what they do.
for loops are called determinate loops because the number of times that the
loop will be executed can always be predicted (hence determined) before
the loop begins. This does not mean that the number of times a certain loop
executes is always the same; the number is often controlled by, or based on, the
values of other variables in the program.
For example the loop to print the 12 times table:
for (int i = 1; i <= 12; i ++ ) {
System.out.print (i);
System.out.print(" x 12 = ");
System.out.println(i * 12);
}

might be more useful if the user could choose how many terms of the table
to see:
int terms;
/* get the number of terms – in reality this number
would also be validated with a conditional statement */
System.out.print ("Enter the number of terms: ");
terms = Console.readInt ();
// display the table
for (int i = 0; i < terms; i ++ ) {
System.out.print (i);
System.out.print (" x 12 = ");
System.out.println (i * 12);
}

Making things happen. Again and again

207

It is not possible to forecast how many times this loop will be executed before
the program is run, but the number will always be known immediately before
the loop starts.
The loop could easily be extended to allow the user to pick the first value in
addition to the number of terms:
int start;
int terms;
System.out.print ("Enter the first value: ");
start = Console.readInt ();
System.out.print ("Enter the number of terms: ");
terms = Console.readInt ();
/* these values should be validated here, and the
loop executed only if they are sensible! */
for (int i = start; i <= start + terms; i ++ ) {
System.out.print (i);
System.out.print (" * 12 = ");
System.out.println (i * 12);
}

Finally, some specialised users might require only alternate terms in the table:
for (int i = start; i <= start + terms; i += 2) {
System.out.print (i);
System.out.print (" * 12 = ");
System.out.println (i * 12);
}

for loops are used when the number of times that a loop will be executed
can be determined for certain before the loop is executed for the first time. If this
number cannot be determined then the alternative, an indeterminate loop, must
be used instead.

while loops
A while loop is indeterminate; this means that it causes statements to be
executed while some Boolean condition is true. The condition can be anything
that produces a Boolean value, much like the condition of an if statement.
A while loop is started with a line of the form:
(while ()
and the program statement or statements affected by the loop appear immediately below enclosed with the familiar braces:
(while () {
// statements
}
The statements are of course indented to make it clear that they are inside the
loop. As with a for loop, any valid statements may appear inside the loop,
including other loops.
The statements will continue to be executed as long as the condition in the
loop is true. When the final statement in the sequence is reached control returns
to the first, and so on. This means that:


the condition must at some point become false or the loop will never
terminate;

208




How to program using Java

if the condition is not true the first time the loop is to be executed the
statements will never be executed at all;
the programmer must be confident of the value of the condition every
time the loop is reached, including the first.

while loops are often used to make sure that the values entered by users are
as expected. If the user enters an invalid value a while loop can be used to provide an error message and repeat the prompt until an acceptable value is provided:
int aNumber;
System.out.print ("Enter a number between 1 and 10: ");
aNumber = Console.readInt ();
while (aNumber < 1 || aNumber > 10) {
System.out.println ("Error: Number out of range");
System.out.print ("Enter a number between 1 and 10: ");
aNumber = Console.readInt ();
}

The condition
aNumber < 1 || aNumber > 10

defines the state when the number entered is out of the required range and
therefore invalid. The loop is executed only if the first attempt to enter the
number is invalid and it will repeat until a valid number is entered:
Enter a number between 1 and 10: 15
Error: Number out of range
Enter a number between 1 and 10: 20
Error: Number out of range
Enter a number between 1 and 10: 7

This produces a much neater dialogue between the user and the program. If the
user makes a mistake they have the chance to correct the error immediately
rather than having to run the whole program again.
Care must be taken that the value of the condition changes at some point in
the loop. For example, this loop will never terminate:
int aNumber = 10;
while (aNumber >= 10) {
System.out.print("The number is " + aNumber);
aNumber ++ ;
}

Here the value of aNumber will keep increasing and the value of the Boolean
expression controlling the loop (that aNumber must be greater than or equal
to 10) will always be true. The loop is an infinite loop. In some very unusual
situations an infinite loop is what is required, but such situations are very rare.
If you find yourself with a program that never seems to end, it’s always a good
idea to check for infinite loops.
Sometimes the statements inside a while loop will not be executed at all. If
the condition is initially false, then control skips past the loop and the statements are never executed. There are times, however, when it is clear beforehand
that the statements inside the loop will always need to be executed at least once.
In this case there is an alternative form of indeterminate loop that can handle
this situation more elegantly.

Making things happen. Again and again

209

do … while loops
do … while loops are a specialised form of while loop. In fact any
do … while loop can always be written as a while loop.2 The only advantage
of this type of loop is that the resulting code can be neater. The relationship
between these two types of indeterminate loop is not unlike that between the
if statement and the switch statement – it is possible to get by with only one,
but using both makes for neater programs.
The statements inside a do … while loop are always executed at least once.
The loop is once again controlled by a condition but this time the condition is
tested only after the statements inside the loop have been executed; if the condition is true then the loop is executed once again. The format of the loop makes
the location of the test clear:
do {
// statements
} while ();
As is now very familiar, the statements inside the loop are enclosed with braces
and are indented. Also as usual, the statements may include any valid Java
statements, including other loops.
do … while loops may also be used to validate values entered by a user.
Their use for this is reasonable since the user is always going to have to enter
the value at least once and therefore the statements in the loop will always be
executed at least once.
A do … while loop can be used to validate user input:
do {
System.out.print ("Enter a number between 1 and 10: ");
aNumber = Console.readInt ();
if (aNumber < 1 || aNumber > 10) {
System.out.println("Error: Number out of range");
}
} while (aNumber < 1 || aNumber > 10);

This is obviously very similar to the example using a while loop and there is
little to choose between the two. Both include some duplication of code; the
while loop example has two System.out.print statements and this version
has the condition tested twice. The two are equivalent; the choice of which to use
in a particular application would come down to the usual matter of style and
taste.3
This observation applies to most cases where an indeterminate loop is used.
If it is obvious that the loop will always be executed at least once then a
do … while loop is the correct choice, but many programs could equally be
written with either form of loop.

A final warning about == and =
Finally, a word about a very common error. Very often an indeterminate loop
executes as long as some variable has a particular value. This is most commonly

2
3

And indeed, vice versa.
It’s interesting that I tend to prefer the do … while version but everyone I work
with seems to use the while version. It really is just down to style and preference –
programming style can be a very personal thing.

210

How to program using Java

the value of some Boolean variable:
boolean finished = false;
while (finished == false) {
// statements
}

or in general:
while (ϽvariableϾ ϭϭ ϽvalueϾ) {
// statements
}
A very common error and careless error is to write loops of this form as:
while (ϽvariableϾ ϭ ϽvalueϾ) {
// statements
}
or
do {
// statements
} while (ϽvariableϾ ϭ ϽvalueϾ);
Neither of these loops will behave as expected. If the variable is any type other
than boolean the program will fail to compile. The compiler spots that = has
been used instead of == and points this out. On the other hand, if a boolean has
been used the program will compile correctly, but will then fail to work as
expected when run.4
The problem is that the = (is assigned) operator has been used instead of
the == (equals) operator. If you find that one of your programs has a loop that
is not behaving as expected this is always something to check. The correct loops
are of course:
while (ϽvariableϾ ϭϭ ϽvalueϾ) {
// statements
}
and
do {
// statements
} while (ϽvariableϾ ϭϭ ϽvalueϾ);
The same error can, of course, also crop up in any condition, and particularly
in conditional statements. The trick to avoid this is to get into the habit of never
writing:
finished == true

4

The reason for this distinction is very subtle indeed. The assignment operation actually
returns the value assigned so a statement like "finished = true;" returns true, which
is the correct Boolean type to use in this place. A statement someNumber =0; returns an
integer, which is not the correct type, and so the compiler spots the error. Java is actually
very good at spotting these errors; C or C++ would allow these errors with any type.

Making things happen. Again and again

211

but always using:
finished

which amounts to exactly the same thing (and saves typing). The same
applies to:
finished == false

and:
!finished

The shorter form is probably always to be preferred.

Flow of control
Armed with loops and conditional statements you can now write programs that
have a complex flow of control. While your programs still execute in a general
sense from top to bottom you now have much more control over precisely what
is going on.
This is a very basic skill of programming, so it’s time to get some practice in
with some examples and exercises.

Example 1 – Buddy’s tables revisited
Buddy is very happy with his “Times Table” program, but the contents of his
main method are starting to worry him. There seems to be a lot of repetition, and he is
wondering if a loop could make his program neater.
The question arises of whether he is correct …
Buddy’s program calculated the first six terms of a times table; the number of
the table required was provided as a command line argument. The body of the
main method looked like this:
TimesTable tt = new TimesTable ();
int num = Integer.parseInt (args[0]);
tt.printLine
tt.printLine
tt.printLine
tt.printLine

(1,
(2,
(3,
(4,

num);
num);
num);
num);

212

How to program using Java

tt.printLine (5, num);
tt.printLine (6, num);

This is indeed an obvious case where a loop can be used. The first parameter of
the printLine method is clearly varying from 1 to 6, and this can easily be
replaced with the control variable of a suitable loop. The loop is determinate
since we know that it will always execute exactly 6 times, so this is a case where
a for loop is appropriate.
The 6 statements printing the tables are simply replaced with:
for (int i = 1; i <= 6; i ++ ) {
tt.printLine (i, num);
}

This solution is shorter and neater and, as we will see in a moment, is much
easier to extend.

Example 2 – Elvis’s harder tables
Elvis is impressed with Buddy’s program but wants a program that will do more.
He wants a program that will print 13 terms of each table (starting at “0 times” and
finishing with “12 times”. He also wants to print out complete versions of the first
13 tables so that he can get some revision in.
He decides to use Buddy’s program as the basis for his own. What does he need to
change?
Two loops are actually needed in this program. One will count the number of
tables and one will count through the terms of the tables. Since both loops are
always required to execute exactly 13 times this is a clear case where a for loop
should be used for both.
There are also two changes needed to Buddy’s program. The first will make
the program count up to “12 times” rather than the current 6, and the second
will make the program repeat for a number of tables. When more than one
change is to be made to a program it is always a good idea to make changes
to a program one at a time. This means that any error that occurs will always
have been caused by the most recent change; it should therefore be easier to
spot, isolate, and correct.
The first stage is therefore to alter Buddy’s program so that 12 terms are
printed for each table. With the changes just made, this is trivial as the only
change needed is in the loop which becomes:
for (int i = 0; i <= 12; i ++ ) {
tt.printLine (i, num);
}

This could equally well be written like this:
for (int i = 0; i < 13; i ++ ) {
tt.printLine (i, num);
}

which is the style that many Java programmers (particularly those brought up
on C or C ϩ ϩ ) might prefer; it’s just the usual matter of style and preference.

Making things happen. Again and again

213

Now we come on to add Elvis’s requirement for many tables. Once again we
can forecast in advance how many tables will be printed; this is obviously
another determinate loop. Another for loop is needed.
The code looks a little complicated, but it can be built up gradually. The first
step might be to include some neat headings for each table, so this loop would
print out some headings for each of the tables:
for (int table = 0; table < 13; table ++ ) {
System.out.print("The ");
System.out.print(table);
System.out.println(" Times Table";
}

It’s worth noting in passing here that there is no need for three separate
System.out.print lines here; it’s just done like this to keep the lines on the
page! The < 13 here is another matter of preference. Since the loop is intended
to count up to 12 you might prefer:
for (int table = 0; table <= 12; table ++ )

which amounts to exactly the same thing. In this case the control variable has
been given a meaningful name; this becomes a very good idea when there is
more than one loop in a program as there is here.
We already have a loop to print out a particular times table. In the current
program the number of the table required is stored in the variable num which is
obtained via a command line argument:
for (int i = 0; i < 13; i ++ ) {
tt.printLine (i, num);
}

The trick in writing Elvis’s program to print out a collection of tables is to put
the second loop inside the first. As a first step to doing this, this pair of loops
will print out the required times table 13 times:
for (int table = 0; table < 13; table ++ ) {
System.out.print("The ");
System.out.print(table);
System.out.println(" Times Table");
for (int i = 0; i < 13; i ++ ) {
tt.printLine (i, num);
}
}

Again the indentation clearly shows that the inner loop is inside the other. The
final fix is to make the number of the table that is to be generated change
for each execution of the outer loop. This is easily done since the number of
the required table is actually the control variable of the outer loop, table. The
inner loop will use this value rather than the command line argument. The
command line argument is now in fact not used at all.
This gives the final loops that would be used in a complete program:
for (int table = 0; table < 13; table ++ ) {
System.out.print("The ");
System.out.print(table);
System.out.println(" Times Table");
for (int i = 0; i < 13; i + + ) {