The Average Examples: Part 1

Averaging without loops and a digression into arithmetic change operators

The examples on this page are designed to introduce the concept of loops. As before, they can be found in the directory
/import/teaching/BSc/1st/ItP
this 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.

Averaging with a while loop

But back to loops. One reason for using loops is to avoid situations like the above where the same piece of code is written several times. The main reason, however, is to generalise programs. The program in 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 20
each 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");

Averaging with a do loop

Suppose you do not know how many numbers you are going to have in advance. Another way of dealing with reading and adding a list of numbers would be to ask after each one whether there are any more. Here's a program which uses that method to calculate an average:
 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.

Loops within loops

A problem with the program in class 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.

Averaging with a sentinel

It can be annoying having to keep answering the "Do you want to continue?" prompt. A simple way to avoid this is to use the idea of a sentinel. This is a value which you know cannot occur in the actual data you are entering but is put at the end of the list of data values. When it is encountered in the execution of the program, it is taken as an indication that all the data has been entered. Below is a program, found in file 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.

Constants

The program in 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.
Matthew Huntbach

These notes were produced as part of the course Introduction to Programming as it was given in the Department of Computer Science at Queen Mary, University of London during the academic years 1998-2001.

Last modified: 29 Sep 1998