/import/teaching/BSc/1st/ItPthis time in subdirectory
averages
A loop is a piece of code which is designed to be executed repeatedly in a program. Calculating averages enables us to consider some simple examples. First of all, let's consider a program that calculates averages but does not use loops:
1 import java.io.*; 2 3 class Average0 4 { 5 // A program that prompts for five integers and calculates and prints their 6 // average 7 8 public static void main(String[] args) throws IOException 9 { 10 BufferedReader in = Text.open(System.in); 11 int sum=0; 12 System.out.print("Enter number 1: "); 13 sum+=Text.readInt(in); 14 System.out.print("Enter number 2: "); 15 sum+=Text.readInt(in); 16 System.out.print("Enter number 3: "); 17 sum+=Text.readInt(in); 18 System.out.print("Enter number 4: "); 19 sum+=Text.readInt(in); 20 System.out.print("Enter number 5: "); 21 sum+=Text.readInt(in); 22 System.out.println("Average is: "+Text.writeDouble((double)sum/5,5,3)); 23 }It is very limited as it can only calculate the average of exactly five numbers. One point of interest to note is line 13 (exactly the same as lines 15, 17, 19 and 21). This is equivalent to writing:
n=Text.readInt(in); sum=sum+n;assuming variable
n
of type int
has already been
declared. In general, a+=b
is a shorthand way of writing
a=a+b
. It causes the value of a+b
to be calculated
and put into variable a
. You can see how =
is not
an equality operator in Java, otherwise a=a+b
wouldn't make
sense. =
is an operator which changes the value of the
variable on its left-hand side, replacing it by the value calculated on its
right hand side (which may make reference to the old value of that variable).
While in a+=b
the left-hand side a
must be a
variable, the right-hand side b
can be any expression. In line
13 it is the expression Text.readInt(in)
which returns the
next integer read from the command window - this value could have first been
put into a separate variable like n
above, but there was no
need.
In the same way, a*=b
is a shorthand for a=a*b
and
so on for the other arithmetic operators. Note this should strictly be
a=a*(b)
. Suppose we had the expression a*=b+c
.
The result of this expression is to add the values in b
and
c
, multiply the value in a
by the result, and
put the result of that in a
. You could say less accurately
as it confuses the two different concepts of a variable and the value in
that variable that a*=b+c
multiplies a
by
b+c
. So it is not equivalent to a=a*b+c
, but
to a=a*(b+c)
- consider the effect of operator precedence on
the first of these.
Average0
could only be used to average exactly five integers.
It would be silly to have to have a separate program to average six integers,
one to average seven, and so on. Here is one which can average any number
of integers:
1 import java.io.*; 2 3 class Average1 4 { 5 // A program that calculates the average of a series of integers. 6 // It asks how many integers there are in total, then prompts for each one. 7 8 public static void main(String[] args) throws IOException 9 { 10 BufferedReader in = Text.open(System.in); 11 int sum=0,count=1,number; 12 System.out.print("Type the number of integers that will be entered: "); 13 number=Text.readInt(in); 14 while(count<=number) 15 { 16 System.out.print("Enter number "+count+": "); 17 sum+=Text.readInt(in); 18 count++; 19 } 20 System.out.println("Average is: "+Text.writeDouble((double)sum/number,5,3)); 21 } 22 }The loop is on lines 14-19. It causes the code on lines 16-18 to be repeated. The exact number of times it is repeated is a run-time decision, i.e. it will depend on factors when the program is actually run, in this case what the user of the program types in when line 13 is executed.
A loop of the form
while(condition) { code }works by first testing the
condition
. If it evaluates
to true
, it executes the code
, then tests
the condition
again, and it keeps on doing this until
the condition
evaluates to false
.
Conditions may include simple arithmetic tests, using <
and
>
. These have lower precedence than the arithmetic operators,
so, for example, a+b<c+d
first adds the values in variables
a
and b
then adds values in variables c
and d
, and evaluates to true
if the result of the
first addition is less than the result of the second, false
otherwise. a<=b
tests whether the value of a
is
less than or equal to that of b
, and a>=b
tests
whether it is greater than or equal to it. Note that the test for whether
two numerical values a
and b
are equal is
a==b
, using two equals characters, as the single equals character
is used to mean assignment. The test for whether they are not equal is
a!=b
.
The actual layout of the code does not have to be in the form given, since
the Java compiler does not take any account of how many spaces or new
lines you put in, but it is strongly recommended you stick to the layout
suggested or to a similar pattern. The reason for this is that it makes
it clear just on casually looking at it that lines 16-18 are part of the
while loop, with the whole of lines 14-19 forming one structure. In fact
it is a good idea to think of the execution of the main
method
as:
Do line 10 Do line 11 Do line 12 Do line 13 Do lines 14-19 Do line 20each of these being one statement. Lines 14-19 are a single compound statement which is made up of the word
while
,
the condition expression in brackets, and the body in lines 15-19.
Adding spaces before the command on a line to show where it fits in the
structure is known as indenting.
Note that if the code in a while loop consists of just one statement it is permissible to leave out the brackets. For example:
1 int i=0; 2 while(i<10) 3 System.out.println("Next integer is "+(i++)); 4 System.out.println("That's it");has lines 2-3 forming the while loop, so line 3 is repeatedly executed until variable
i
holds 10, then finally line 4 is executed once.
This example also shows a use of Java feature which originally came from C.
The expression i++
has a dual role - it evaluates to the
value stored in variable i
and then has the side-effect
of changing that value by increasing it by 1. This can be used to make code
more compact, so the above is equivalent to:
1 int i=0; 2 while(i<10) 4 { 5 System.out.println("Next integer is "+i); 6 i=i+1; 7 } 4 System.out.println("That's it");
1 import java.io.*; 2 3 class Average2 4 { 5 // A program that calculates the average of a series of integers. 6 // It prompts for each one then asks if there are any more. 7 8 public static void main(String[] args) throws IOException 9 { 10 BufferedReader in = Text.open(System.in); 11 int sum=0,count=0; 12 char ch; 13 do 14 { 15 count++; 16 System.out.print("Enter number "+count+": "); 17 sum+=Text.readInt(in); 18 System.out.print("Do you want any more numbers? (y/n): "); 19 ch=Text.readChar(in); 20 } 21 while(ch=='y'); 22 System.out.println("Average is: "+Text.writeDouble((double)sum/count,5,3)); 23 } 24 }This uses another sort of loop, called a do loop. The only difference between a do loop and a while loop is that a do loop always executes the body of the loop at least once. Its general form is:
do { code } while(condition)A while loop tests the condition first, and so the code would not be executed even once if the condition evaluated to
false
the
first time round. A do loop executes the code first, and then tests the
value of the condition, then, like the while loop, if the condition
evaluates to true
it goes back and executes the code
again, otherwise execution goes on to the next statement after the do
loop. The only reason for using a do loop would be when you know you
definitely want the code to be executed at least once.
Note this program shows the ==
operator can be used for
characters as well as integers, as it is on line 21. The word char
in Java is used to declare a character variable - done here on line 12. This
is a variable that holds a single character rather than an integer or floating
point number or String of characters as we have seen previously. A character
constant is shown by surrounding the character question by single quotes, for
example 'y'
on line 21. So line 21 says the loop is exited
if when execution gets to this point, the variable named ch
holds the value 'y'
. The class Text
has a
readChar
method (used on line 19) similar to the
readInt
, readString
and readDouble
methods we have seen previously.
On line 15, the program in Average2.java
makes use of the
++
operator mentioned above. In this case, the value in the
variable count
needs to be increased by 1 before it
is used, so we could not condense lines 15 and 16 into the one line:
System.out.print("Enter number "+(count++)+": ");There is, however, another form of
++
we could use. We could
condense lines 15 and 16 into:
System.out.print("Enter number "+(++count)+": ");The expression
++i
where i
is an integer variable
is very like the expression i++
in having both a value and a
side-effect of increasing the value in i
by 1. However,
++i
does the side-effect before producing the value, so the
value of ++i
is the value which was in i after it has
been increased by 1, and like i++
the increase by 1 remains
after the value has been used.
In this case, it would probably not be a good idea to condense lines 15
and 16 in the way shown above. It makes the program clearer to understand
if the increase of the value in count
is shown clearly on a
line of its own rather than mixed in with the printing of the prompt.
It is a good point not to try and be "clever" and show off your knowledge
of a programming language by using features in a way that is possible but
does nothing to enhance the understandibility of the code. Programs should
always be written in a way that helps others easily understand what they
do (remember that professional programmers spend more time modifying
code that has already been written than they do writing new code from
scratch).
When writing loops it is important to ensure there is something that
changes the value in a variable which occurs in the condition of the loop
so that the condition eventually becomes false
and the loop is
exited. In Average1
this is line 18, in Average2
,
line 19. It is not uncommon for programs to contain mistakes which means a
loop condition never becomes false
, resulting in an
infinite loop. Then if the program is run it will never terminate.
It is important to know how to get out of this situation should it ever
occur when you are developing your own programs. Generally pressing
"control-C" (the key marked "Control" and the letter "C" key together) in
the window where you started running the program will do it. Later we
shall see that Java does have ways to exit loops other than failing the
loop condition - Java exceptions and the break
operator.
Average2
is that while
it prompts for a y
or n
answer, in fact if any
character except y
is entered, the loop will continue. There is
no check to see whether just y
or n
has been
entered. Below is a program that makes such a check:
1 import java.io.*; 2 class Average3 3 { 4 // A program that calculates the average of a series of integers. 5 // It prompts for each one then asks if there are any more. 6 7 public static void main(String[] args) throws IOException 8 { 9 BufferedReader in = Text.open(System.in); 10 int sum=0,count=0; 11 char ch; 12 do 13 { 14 count++; 15 System.out.print("Enter number "+count+": "); 16 sum+=Text.readInt(in); 17 do 18 { 19 System.out.print("Do you want any more numbers? (y/n): "); 20 ch=Text.readChar(in); 21 } 22 while(ch!='y'&&ch!='n'); 23 } 24 while(ch=='y'); 25 System.out.println("Average is: "+Text.writeDouble((double)sum/count,5,3)); 26 } 27 }This program shows an example of a loop inside a loop. It is important to think in terms of the structure of the program - do not think of it just as a list of lines of code. The
main
method should be
thought of as consisting of five statements - the first three are the
declarations (including initialisations) on lines 9, 10 and 11, the fourth
statement is the do loop which occupies lines 12-24 which reads a series of
numbers and adds them, the fifth statement is the one on line 25 which does
the division and prints out the average.
The do loop on lines 12-24 contains four statements in its body. The first
which is on line 14 increases the value in variable count
by 1.
The second, on line 15, prints the prompt for the next number. The third,
on line 16 reads the next number and adds it to the value in variable
sum
. The fourth is the inner do loop on lines 17-22 which
keeps asking the user to type in a character until either y
or
n
has been typed.
The indentation of the code helps show its structure. You can see that lines
12-24 are meant to form one unit by the way the code in between is indented by
three spaces. You can see that lines 17-22 are meant to form one unit within
that by the way the lines in between are indented a further three spaces.
The Java compiler itself pays no attention to the layout of the code, so
you could have written lines 7-26 in the program Average3
above
as:
public static void main(String[] args) throws IOException { BufferedReader in = Text. open(System.in); int sum=0, count =0; char ch; do { count++; System.out.print("Enter number "+count+": "); sum+=Text.readInt(in); do { System.out.print("Do you want any more numbers? (y/n): "); ch=Text.readChar(in); } while(ch!='y'&&ch!='n'); } while(ch=='y'); System.out.println("Average is: "+ Text.writeDouble((double)sum/count,5,3));}and it would compile and run just the same, but it should be obvious that it's much harder to understand how the program works if it is written with such a perverse layout. Just so you can see this still works, the program laid out in this manner is given in the file
Average3_silly.java
.
Another thing to note in Average3
is the use of a compound
conditional. On line 22 you will see the conditional for the inner do
loop is (ch!='y'&&ch!='n')
. Two or more conditions may be
joined together by using the &&
operator, which means
and. So, in general a&&b
evaluates to
true
if a
evaluates to true
and b
evaluates to true
. So
(ch!='y'&&ch!='n')
evaluates to true
if the
first condition in the compound, ch!='y'
is true, and the
second condition in the compound ch!='n'
is also true, in other
words if ch
holds a character which is neither y
or n
. So lines 17-22 are exited only when this point is
reached in executing the code, and ch
does hold either
'y'
or 'n
. At this point, as there is no
other statement within the outer do-loop following line 22, the conditional
in the outer loop is tested, and execution drops out of that loop as well
if the character entered and stored in variable ch
is not
'y'
.
The operator written ||
is used to mean or. So, in
general a||b
evaluates to true
if
a
evaluates to true
or b
evaluates to true
. Note that &&
has higher
precedence than ||
, so a&&b||c
is interpreted as (a&&b)||c
and not as
a&&(b||c)
.
Note also that Java does have operators &
meaning
"and" and |
meaning "or", but these have subtle differences
with the doubled character &&
and ||
operators,
and it is recommended you use the latter for now.
Average4.java
that works in this way:
1 import java.io.*; 2 3 class Average4 4 { 5 // A program that calculates the average of a series of integers. 6 // It prompts for each one. A sentinel value is used to indicate the 7 // end of the series. 8 9 public static void main(String[] args) throws IOException 10 { 11 BufferedReader in = Text.open(System.in); 12 int sum=0,count=1,n; 13 System.out.print("Enter number 1 (or -999 to finish): "); 14 n=Text.readInt(in); 15 while(n!=-999) 16 { 17 sum+=n; 18 count++; 19 System.out.print("Enter number "+count+" (or -999 to finish): "); 20 n=Text.readInt(in); 21 } 22 System.out.println("Average is: "+Text.writeDouble((double)sum/(count-1),5,3)); 23 } 24 }The sentinel value used is
-999
. Note we have to be quite
careful with our code here. The sentinel value is not part of the data,
so we have to make sure it is not added to the sum: it might make quite a
big difference to the average if a large negative number were added to the
data. In fact the way the loop works is that the next number to be added
is read at the end of the loop on line 20, but it is actually
addeed next time round the loop on line 17. Either that, or the number is
the sentinel so execution exits the loop without adding it. Because of this,
it is necessary to have an initial readong of the first number before
the loop starts, on line 14. Because the variable count
is one
greater than the number of integers read in when execution exits the
loop (it would have been increased by one extra when the sentinel was
read), the division to find the average on line 22 is actually by
count-1
.
While all these are small things, they are things where errors can creep
into a program. Counts being off by one are a particularly common thing
in programming, and sometimes can cause quite subtle errors which are not
easy to spot. You might run the program and not notice that your average
was consistently reported as slightly lower than it should be due to not
noticing this problem of count
being off by one.
Another thing to note in this program is that it could potentially result
in a division by zero. You probably know that in mathematics dividing
zero by zero is meaningless, and dividing anything else by zero gives
infinity. In many programming languages, attempting to divide by zero
causes a run-time error often called a "crash", meaning execution of the
program halts and an error message (not always one that is useful) is printed
in the command window where you started the program. In Average4
if the first number you enter is -999
, then you are asking for
the average of zero numbers, and the division on line 22 is of zero by
zero. If you run the program and type in -999
at the first
prompt, you will find that the program does not crash, but a rather mysterious
question mark is printed in the place of the average. In fact Java has a
special value which is stored in floating point variables when the result
put into them comes from a division by zero. We won't discuss this in more
detail here.
Average4.java
has one aspect which is generally
considered to be bad programming practice. To show it, suppose we discovered
that the data being averaged could easily include the number -999 (maybe
it is trying to find the average student bank balance, with negative
figures indicating an overdraft...). Obviously we could not then use -999
as a sentinel. To change it, we would have to go through the whole program
changing -999 to whatever we chose as the new sentinel. But that would cause
further problems if it happened to be the case that we used -999 somewhere
else for some other reason, so we would have to look carefully each time
to ensure every -999 really was being used as a sentinel. That may be easy
in a small program such as our examples, but imagine doing it in a real
program being used in industry, which would be much, much larger. The
"millenium bug" problem illustrates just what difficulties are caused
when programs are written under one assumption (in that case, that dates
beyond the year 1999 would never be part of the data) but have to be changed
when that assumption no longer applies.
It is considered good programming practice not to use numbers inside a
program, but rather to use a variable in their place which is initialised
to the number in question. That way, if the number is changed, rather than
have to go through the whole program, all that is necessary is to make the
single change in the initialisation. Here is a version of Average4
which works that way:
1 import java.io.*; 2 3 class Average5 4 { 5 // A program that calculates the average of a series of integers. 6 // It prompts for each one. A sentinel value is used to indicate the 7 // end of the series. 8 9 static final int SENTINEL = -999; 10 11 public static void main(String[] args) throws IOException 12 { 13 BufferedReader in = Text.open(System.in); 14 int sum=0,count=1,n; 15 System.out.print("Enter number 1 (or "+SENTINEL+" to finish): "); 16 n=Text.readInt(in); 17 while(n!=SENTINEL) 18 { 19 sum+=n; 20 count++; 21 System.out.print("Enter number "+count+" (or "+SENTINEL+" to finish): "); 22 n=Text.readInt(in); 23 } 24 System.out.println("Average is: "+Text.writeDouble((double)sum/(count-1),5,3)); 25 } 26 }The variable that is initialised to the sentinel value is
SENTINEL
and the initialisation is on line 9. Then SENTINEL
rather than
-999
is used in the rest of the program, on lines 15, 17 and 21.
Note that in lines 15 and 21 the original -999
was part of a
string rather than an integer, so additional changes were necessary (the
letters SENTINEL
inside the quotes of a string would not represent
the value inside the variable SENTINEL
). Another thing to
note is that the variable SENTINEL
is declared as
static final
, and is declared outside the main
method or any other method. Declaring a variable outside any method means
that it is shared by all methods in the class. It's not really an issue
in this class which only has the one method; the declaring of variables
outside methods will be discussed in greater detail later, as will the use
of the word static
in this variable declaration. The word
final
means that the variable is given the value -999 when the
program starts running and may never have that value changed while the
program is running. If you wrote an assignment statement to
SENTINEL
inside the program, you would get an error message when
you tried to compile it. This is just an additional precaution to make
sure if the program is altered later the person who is altering it realises
that the value of SENTINEL
is meant to be fixed. The effect is
that SENTINEL
is a constant. It's a common practice,
which we have observed here, to give constants names which are made up
entirely of capital letters, though Java does not enforce that practice.
Now if you found out that -999 was not a suitable sentinel value, all you
would have to do is change it on line 9 to whatever new value you decided
was suitable.
Last modified: 29 Sep 1998