Week 05

Looping with For
Version 1


 


Going Around In Circles

 

Conditional statements are a very handy thing, we couldn't write many useful programs without them. But they are only of limited value. Even with if the flow through a program is still essentially downwards. You can choose between two downward paths, but they are still downward paths. Many times we want to do something several times and therefore want to be able to jump backwards (or upwards) in the program to some previous point. This is called a loop.

For example, suppose we wanted to write a program that would get 10 grades from the user and compute the average. One possible top-down-design for this is:

Program to average 10 grades typed by the user

Tools:
Input/Output Routines

Ingredients:
Floating Point Variables: grade, sum, average

Steps:

  1. Get the 10 grades from the user and print the average
    1. Set the sum to 0
    2. Input the 10 grades, computing the sum as you go
      1. Get the first grade and add it to the sum
      2. Get the second grade and add it to the sum
      3. Get the third grade and add it to the sum
      4. Get the fourth grade and add it to the sum
      5. Get the fifth grade and add it to the sum
      6. Get the sixth grade and add it to the sum
      7. Get the seventh grade and add it to the sum
      8. Get the eighth grade and add it to the sum
      9. Get the ninth grade and add it to the sum
      10. Get the tenth grade and add it to the sum
    3. Compute the average by dividing the sum by 10

But this is not a great solution. It's going to be a pain to type in as it is, and if we wanted to modify it to compute the average of 100 grades it would be really horrendous. A better design would be:

Better program to average 10 grades typed by the user

Tools:
Input/Output Routines

Ingredients:
Floating Point Variables: grade, sum, average

Steps:

  1. Get the 10 grades from the user and print the average
    1. Set the sum to 0
    2. Input the 10 grades, computing the sum as you go
      1. Get a grade
      2. Add it to the sum
      3. If this is not the tenth grade, go back to the step two lines up.
    3. Compute the average by dividing the sum by 10

The Old Way: Goto The question is, how do we "go back to the step two lines up"? In the old days this was accomplished with something called a goto statement. You would put a special marker called a label at the beginning of the part of your program you wanted to repeat. Then at the point that you wanted to go back to the begining of that part you would put a goto statement saying which label (since there could be many of them in a program) you wanted to jump to.

The problem is that goto's got used for alot more than just loops, and sooner or later you'd have goto's all over the program pointing every which way, which could make a program just about impossible to understand. This problem of unmanageably tangled programs became known as spaghetti code, and lead to a seminal 1968 paper by Edsgar Dijkstra (Dike-strah) called Goto Statement Considered Harmful.

The Modern Way: Loop Constructs Dijkstra's paper is credited with jump-starting the transition to what are now called structured languages, such as Pascal, C, Ada, Java, and modern dialects of Fortran. The solution used by structured languages is to de-emphasize the use of goto (though many languages, including C++, still have restricted versions of it) in favor of specific loop constructs.

In Java there are, in fact, 3 different loop constructs available. Which you use depends on the circumstances. The rest of this week's notes will focus on one of these constructs. The remaining two are covered next week.


Counter Loops With For

  The most common situation in which you need a loop is one in which you want to do something a predetermined number of times, as in the example above where you want to repeat the process of getting a grade and adding it to the sum 10 times. The construct used for this is the for loop, which has the following syntax:

for (initialization ; test ; increment) {

  first statement to do inside loop;
  ...
  last statement to do inside loop;

}

The three expressions in the parentheses together specify the control information for the loop. The first part initializes the variable you will use to control the number of times you want the loop to execute. This variable is called the index variable of the loop. The second part is a test to perform each time you begin to go through the body of the loop. If this test is true, then you proceed into the loop. If it fails, you skip over the body of the loop and continue on with the rest of the program. The last part is something to do each time after going through the body of the loop. After it has been done, control returns to the test at the top of the loop.

So, if you wanted to print the numbers from 1 to 5, you could use the following code:

for (int count = 1 ; count <= 5 ; count = count + 1)  {
    HMCSupport.out.println(count);
  } 

It starts by declaring the int variable count and setting it to 1. It then checks if count is less than or equal to 5. Since it is, flow enters the loop. In this case the loop only does one thing, print out the current value of count, which is 1. It then does the increment command, adding one to the value of count, making it 2, and returns to the top to perform the test again. The test succeeds again (since 2 is less than or equal to 5) and the process repeats. Eventually, after three more passes through the loop, when count is 5, that value is printed and the increment step sets count to 6. Control returns to the test at the top of the loop, but the test fails. So execution continues with whatever code follows the loop.

The most common counter controlled loop pattern is one like this last one in which you count from some value to another by ones. But there are many other possibilities. For example, we could count from -50 to -100 by twos:

for (int count = -50 ; count >= -100 ; count = count - 2)  {
    HMCSupport.out.println(count);
  } 

Note that since this loop counts down, we have reversed the sense of the continuation test from the last example. We use >= instead of <=.

The Loop Bounds Need Not Be Fixed Of course, when we say that the number of passes through the loop is fixed, we do not mean that it must be known at compile time. We could for instance have the loop bounds determined at runtime by some value typed by the user:

int low, high;
HMCSupport.out.print("Enter lower and upper bounds for the loop: ");
low = HMCSupport.in.nextInt();
high = HMCSupport.in.nextInt(); 
for (int count = low ; count <= high ; count = count + 1)  {
    HMCSupport.out.println(count);
  } 

The Scope Of The Loop Control Variable In all these examples, we have declared the variable whose value controls the loop as part of the initialization step in the loop-control header. While this is not necessary (the variable could be declared at the beginning of the method with the other variables), it is good style.

When the variable is declared in the loop control header its scope is restricted to the loop itself. It can only be referred to in the other parts of the header, and in the statements in the body of the loop.

As with other variables, you should pick the most descriptive name possible. What is your loop counting? In this case, not much, so we picked the non-descript name count. In the grade-averaging program below, each pass through the loop corresponded to getting another grade value from the user, so we name the variable gradeNum.

When There Is Only One Statement As with if the brackets are optional if the body of the loop is a single statement, like in these examples. But, as with if we recommend using the braces in any case.

Averaging 10 Grades With A For Loop To return to our original example of averaging ten grades, we arrive at the following design:

Better program to average 10 grades typed by the user

Tools:
Input/Output Routines

Ingredients:

Floating Point Variables: grade, sum, average
Integer Variables: grade-counter

Steps:

  1. Get the 10 grades from the user and print the average
    1. Set the sum to 0
    2. Input the 10 grades computing the sum as you go
      1. Do the following once for each value from 1 to 10:
        1. Get a grade
        2. Add it to the sum
    3. Compute the average by dividing the sum by 10

This can be translated directly into the following program:

Click Here To Run This Program On Its Own Page
// Program: AverageGrades
// Author:  Joshua S. Hodas
// Date:    October 2, 1996
// Purpose: To demonstrate the use of for loops
 
import HMC.HMCSupport;
 
class AverageGrades {
 
  public static void main(String args[]) {
 
    double grade, sum, average;
 
    HMCSupport.out.println("This program averages 10 grades.");
 
    sum = 0;
    
    for (int gradeNum=1 ; gradeNum <= 10 ; gradeNum=gradeNum+1) {
 
      HMCSupport.out.print("Please enter grade #" + gradeNum
                           + ": ");
      grade = HMCSupport.in.nextDouble();
            
      sum = sum + grade;
    }
    
    average = sum / 10;
    HMCSupport.out.println("The average of the grades is " 
                           + average);
 
  }
 
}

The nice thing about this program is that, if we now want to modify the program to compute the average of one hundred grades, the only thing we need to do is change the test from gradeNum <= 10 to gradeNum <= 100.

Using The Increment Operator Notice the use of the pattern
gradeNum = gradeNum + 1 
in the loop control header. As we mentioned in the notes two weeks ago, this pattern is very common, and can be abbreviated
gradeNum++ 
This allows us to rewrite the header of the for loop in the last program as:
for (int gradeNum = 1 ; gradeNum <= 10 ; gradeNum++) 
This is the most commonly used style of loop header.

Top-Down Design With For Loops As with conditional statements, any arbitrary series of Java statements can go in the block making up the body of the for loop. For example, we could take the simple if example from the last lecture which asked the user for a number and told them whether it was less than ten or not, and run it twenty times by putting it inside a loop from 1 to 20, as in:

Click Here To Run This Program On Its Own Page
// Program: IfLoop
// Author:  Joshua S. Hodas
// Date:    October 4, 1996
// Purpose: To demonstrate the use of if inside a loop
 
import HMC.HMCSupport;
 
class IfLoop {
 
  public static void main(String args[]) {
 
    for (int count = 1 ; count <= 20 ; count++) {
 
      int inputValue;
 
      HMCSupport.out.println("Please enter an integer value:");
      inputValue = HMCSupport.in.nextInt();
   
      if (inputValue < 10) {
 
        HMCSupport.out.println("That value is < 10.");
 
      }
      else {
 
        HMCSupport.out.println("That value is >= to 10.");
 
      }
    }
  }
 
}

We have taken the entire body of the existing program (including the variable declaration) and just made it into the body of our loop. As with the loop-control variable declared in the loop header, the variable inputValue is only accessible inside the loop. If we needed to get access to its value elsewhere, we would move the declaration to the beginning of the method.

The point is that whenever you have a process that calls for doing a given task some number of times, you should be able to break down the design process as requiring you to figure out how to do the task once and then wrapping the result in a loop with the appropriate control. The design of the inner task should not be much affected by the fact that it is part of the enclosing loop.

This ability to separate different aspects of the design is what makes top-down design and structured languages such a good fit.


Nested For Loops

  Just as you can nest an if statement inside a for loop, you can just as well nest a for loop inside a for loop. This sort of structure will naturally result from a variety of designs.

For example, suppose that you want to print out a multiplication table. How do you do it. Well, let's look at the top down design (We'll assume that the table has the same number of rows and columns.):

  1. Ask the user how many rows/columns they want (call it size).
  2. For each number from 1 to size:
    1. Print a row of the multiplication table corresponding to that number

So, the outer structure of the program will be a for loop. We could even write down the code for this much of the program, without knowing how to fill in all the details:

// Program: MultTableSkeleton
// Author:  Joshua S. Hodas
// Date:    October 4, 1996
// Purpose: The skeleton of a multiplication table printer
 
import HMC.HMCSupport;
 
class MultTableSkeleton {
 
  public static void main(String args[]) {
 
    int size;
    
    HMCSupport.out.println("This prints a multiplication table.");
    HMCSupport.out.print("How many rows/columns do you want?");
    size = HMCSupport.in.nextInt();
    
    for (int rowNum = 1 ; rowNum <= size ; rowNum++) {
 
      // Code to print a single row goes here.
     
    }
  }
 
}

Now, how do we print a row of the table?

  1. For each number from 1 to size:
    1. Print the product of that number and the number of the row we are printing, leaving the cursor on the same line for the next output.
  2. Move the cursor down to beginnning of next line

But this is just:

for (int colNum = 1 ; colNum <= size ; colNum++)  {
    HMCSupport.out.print(rowNum * colNum);
  }
HMCSupport.out.println(); 
Combining these together we get:

Click Here To Run This Program On Its Own Page
// Program: MultTable
// Author:  Joshua S. Hodas
// Date:    October 4, 1996
// Purpose: A multiplication table printer (for in for)
 
import HMC.HMCSupport;
 
class MultTable {
 
  public static void main(String args[]) {
 
    int size;
    
    HMCSupport.out.println("This prints a multiplication table.");
    HMCSupport.out.print("How many rows/columns do you want?");
    size = HMCSupport.in.nextInt();
    
    HMCSupport.out.setWidth(3);
    
    for (int rowNum = 1 ; rowNum <= size ; rowNum++) {
 
      // Code to print a single row"
     
      for (int colNum = 1 ; colNum <= size ; colNum++) {
 
        HMCSupport.out.print(rowNum * colNum);
     
      }
      HMCSupport.out.println();
      
    }
  }
 
}

We have added a call to HMCSupport.out.setWidth at the beginning in order to improve the formatting of the table (i.e. to get all the numbers to line up in columns).

Positioning Variable Declarations and Initializations

It is important to always be clear on the scope of your variables. That is, the section of within which you can refer to a given variable. Recall that a variable declared in the header of a loop, or in the body of the loop is only available within the code for the loop.

In deciding the best place to declare a variable you must take into account where you will need to refer to it. If it is only used to store temporary values during the loop (such as the loop index) then it is good style to declare it in the loop header or body. On the other hand, if it is being used to store some value that will be needed after the loop is completed (such as a sum or product that the loop is being used to compute) then it should be declared outside (before) the loop.

A similar issue arises as to where to put the initialization of a variable used in a loop. Typically, the initialization should be before the loop, or, in the case of the loop index, in the loop header. If you put the initialization of a variable insode the loop then the variable will be reset to that value at the start of each cycle through the loop, throwing away any changes that were made to it in the last cycle. This is rarely what you want.

In general, it will be apparent from the top-down-design how to position your declarations and initializations. This is just one of many reasons to start with a proper top-down-design.

Consider the problem of computing the final grades for a number of students based on their homework grades. This is an extension of the grade averager from above. The high-level design looks like:

Program to average grades for a class typed by the user

Tools:
Input/Output Routines

Ingredients:
Integer Variables: numStudents, numHomeworks

Steps:

  1. Ask the user how many students there are (call it numStudents)
  2. Ask the user how many homeworks there are (call it numHomeworks)
  3. For each student from 1 to numStudents:
    1. Get the students' grades, computing the sum as you go, and display the average.

This leads to the shell of the program that looks like this:

// Program: StudentAverageSkeleton
// Author:  Joshua S. Hodas
// Date:    September 20, 1998
// Purpose: The skeleton of a student grade averager.
 
import HMC.HMCSupport;
 
class StudentAverageSkeleton {
 
  public static void main(String args[]) {
 
    int numStudents, numHomeworks;
    
    HMCSupport.out.println("This computes students' average grades.");
    HMCSupport.out.print("How many students are there?");
    numStudents = HMCSupport.in.nextInt();
    HMCSupport.out.print("How many homework assignments are there?");
    numHomeworks = HMCSupport.in.nextInt();
    
    for (int studentNum = 1 ; studentNum <= numStudents ; studentNum++) {
 
      // Code to gather grades and compute average for one student
      // goes here.
     
    }
  }
 
}

Now, the action to be taken for each student corresponds pretty much to the design of our previous program to average 10 grades. The only difference is that instead of their being 10 grades, the number of grades is determined by the value that was entered by the user for the number of homeworks. We can simply insert a slightly modified version of the top-down-design for that problem in place in this design:

Program to average grades for a class typed by the user

Tools:
Input/Output Routines

Ingredients:
Integer Variables:
numStudents, numHomeworks, studentNum

Steps:

  1. Ask the user how many students there are (call it numStudents)
  2. Ask the user how many homeworks there are (call it numHomeworks)
  3. For each student (studentNum) from 1 to numStudents:
    1. Routine to average numHomeworks grades typed by the user

      Ingredients:

      Floating Point Variables: grade, sum, average
      Integer Variables: numGrade
      Steps:
      1. Get the students' grades, computing the sum as you go, and display the average.
        1. Set the sum to 0
        2. Input the numGrades grades computing the sum as you go
          1. Do the following once for each grade (gradeNum) from 1 to numGrades:
            1. Get the grade
            2. Add it to the sum
        3. Compute the average by dividing the sum by numGrades

Thus it is clear that the variables for the inner structure can be declared inside the block for the outer loop, since they are only needed in that block. Similarly, it is clear that we must initialize the sum to 0 each time we start a cycle through the outer loop, since each cycle is for a different students. If we did not initialize the sum to 0 each time through, then each students' sum (and, hence, average) would include the sum from all the previous students. A program realizing this design is:

Click Here To Run This Program On Its Own Page
// Program: StudentAverage
// Author:  Joshua S. Hodas
// Date:    September 20, 1998
// Purpose: A student grade averager.
 
import HMC.HMCSupport;
 
class StudentAverage {
 
  public static void main(String args[]) {
 
    int numStudents, numHomeworks;
    
    HMCSupport.out.println("This computes students' average grades.");
    HMCSupport.out.print("How many students are there? ");
    numStudents = HMCSupport.in.nextInt();
 
    HMCSupport.out.print("How many homework assignments are there?");
    numHomeworks = HMCSupport.in.nextInt();
 

for (int studentNum = 1 ; studentNum <= numStudents ; studentNum++) { double grade, sum = 0, average; for (int gradeNum=1 ; gradeNum <= numHomeworks ; gradeNum++) { HMCSupport.out.print("Please enter grade #" + gradeNum + " for student #" + studentNum + ": "); grade = HMCSupport.in.nextDouble(); sum = sum + grade; } average = sum / numHomeworks; HMCSupport.out.println("The average of the grades for student #" + studentNum + " is " + average); } } }


Exiting Loops Early

  Often it is useful to be able to stop the execution of a loop in the middle. There are two different types of actions that may be desirable.

Skipping The Rest Of One Cycle Under some circumstances, there may be a point midway into the body of a loop in which you would like to skip the remainder of the code in the body of the loop for that one pass through the loop. That is, you wish to jump directly to the bottom of the loop and do the increment step. This is accomplished with the continue statement.

Breaking out of the loop entirely A more common circumstance is a situation in which you wish to abort a loop entirely, continuing execution with the code after the loop. This is done by using the statement break (as in the switch statement). This statement will cause execution of the code in the loop to terminate, and for flow to continue below the loop.

Using Labels With Break And Continue If you are inside the body of a loop that is itself nested in another loop, and you wish to continue or break out of the outer loop, you can use a labelled continue or break statement. In order to do this, a label is used to mark the beginning of the loop you might wish to break out of. The label is simply any valid Java identifier followed by a colon. The name of the label is then given after the word continue or break that you wish it to apply to.

Use These In Moderation We have intentionally not given examples of using either continue or break. The reason is that they should only be used in moderation, when the alternative would be a very contorted piece of code. Under most circumstances, they can be avoided by choosing a better design (like using an if statement to avoid the bit of code you wish to skip over, rather than using a continue statement), or using one of the other loop constructs that will be discussed in the next lecture, instead of using a for loop. There are, however, examples of their use in a number of Java reference books.

Last modified August 28 for Fall 99 cs5 by fleck@cs.hmc.edu


This page copyright ©1998 by Joshua S. Hodas. It was built with Frontier on a Macintosh . Last rebuilt on Sun, Oct 4, 1998 at 1:15:33 PM.
http://www.cs.hmc.edu/~hodas/courses/cs5/week_05/lecture/lecture.html