Session 8: for loops

The for loop (Section 5.2)
    Idioms
    Declarations in the initialization
    Empty clauses
Exercise

The for loop

Textbook: Section 5.2

By now, you've probably grown quite accustomed to writing while loops that go through a sequence of numbers. Say we want to print the word ``Hello'' 2,001 times. The way we would write this (prior to today) is the following.

int i = 0;
while(i < 2001) {
    IO.println("Hello");
    i++;
}
This is something we often want to do - do something for each value in a sequence (in this case, print ``Hello'' for a consecutive sequence of numbers).

Today, I'm here to tell you that there's a better way: Java includes a different looping construct, the for loop, designed for this situation. Using the for loop, the above would instead read:

int i;
for(i = 0; i < 2001; i++) {
    IO.println("Hello");
}
Though the syntax is peculiar and certainly not intuitive, the for loop enables us to put the fact that we want to iterate through a sequence of values of i all on one line - it makes it clear up front to the reader of the program that this is what we're doing, and not something different. (For short loop bodies like this, you may not see the benefit - but for very long loop bodies, it's nice to know up front that we're going through consecutive integers without having to scan down to find the bottom of the loop to make sure.

Here's the for statement decoded: The format of the for is as follows.

for(initialization; test; update) statement(s)
Inside the parentheses is a sequence of three expressions (I'd call them clauses), divided by semicolons.
  1. the initialization clause: what we want to do before we start doing our iteration. In our ``Hello'' example, we want to begin i out at 0, so we put i = 0 into this first part.

  2. the test clause: a Boolean expression that tells us how we should determine whether to continue going through the loop. We wanted to continue going through the loop until i reaches 2,001, so we wrote i < 2001 for our second part.

  3. the update clause: an expression that updates us to refer to the next value in the sequence. In our case, the next value is the next integer, so we have i++ to bring us up to the next value.

Idioms

Seem complicated? The fact is that people only use for loops in constricted circumstances, and you quickly learn the idioms. By far the most common idiom is to count through the integers from 0 to n - 1. And the way people always write that is the following.

for(i = 0; i < n; i++) ...
So if you see something that looks like the above, you don't even have to think through it.

Occassionally, people want to go backwards - starting at n - 1 and going down to 0. The idiom for this:

for(i = n - 1; i >= 0; i--) ...

Two other somewhat common idioms: Going from 1 to n

for(i = 1; i <= n; i++) ...
And going from n down to 1:
for(i = n; i >= 1; i--) ...

Declarations in the initialization

These for loops usually have a control variable of some sort to iterate through the various values. (So far, I've been using i to iterate through values.) Java allows you to declare this variable within the for loop. It's perfectly legitimate for me to write the following.

for(int i = 0; i < 2001; i++) IO.println("Hello");
This is completely self-contained.

You should take advantage of this when you can. Basically, the only case when you can't take advantage of it is when you want to refer to the variable outside the loop, since its final value is important to you.

int i;
for(i = 1; i < n; i *= 2) { }
IO.println(i);
This compact little program iterates through the powers of 2 until I find one that is at least n. (Notice that the body of the for loop in this particular case is empty - I don't have anything to do for each power of 2, I just want to keep going until I reach n.) What's significant here is that we had to declare i outside the for loop, since a declaration inside the initialization expression of the for loop persists only for the duration of the loop, and it isn't available outside the loop. In this case, I wanted to print the final value I reached, so I had to declare it outside.

Generally, however, you'll be able to declare your variables within the for loop. And so you should, since it reduces all the significant components of iterating through values into a single line, making it easier to swallow in one gulp.

Empty clauses

You can leave clauses empty when appropriate. Every once in a while, for example, you'll have a situation where you've computed the initial value beforehand. Then you can just leave the initialization clause blank.

for(; i < n; i++) ...

The other clauses you can omit, too. I don't run into those situations very often, though - usually, a while loop does the job just as well. For example, there's never a reason to include only the test clause: A while loop accomplishes the same thing, and it's clearer.

One thing mentions special merit, though: Some people prefer to write for(;;) ... instead of while(true) ..., thinking to themselves that the for in this instance is short for forever. (When you omit the test clause, the test always returns true as far as the compiler is concerned.) These for(;;) people are weird, but you should know there are some weird people out there. Stick with the while variant - it's clearer.

Exercise

Define a class called Student to represent the grade achieved by a student. You'll want the class to employ two instance variables - one to represent how many points the student has earned, and one to represent the total number of points scored so far.

The class should define the following two methods.

void addScore(int score, int possible)
Registers that the student has scored score points out of a possible possible.

double getGrade()
Prints the fraction of points the student has earned. (Returning 1.0 indicates the student has a perfect 100%.)

You can use the following class definition to test your class.

import csbsju.cs160.*;

public class TestStudent {
    public static void run() {
        Student stud = new Student();

        while(true) {
            IO.print("Option (0 for help)? ");
            int option = IO.readInt();

            if(option == 0) {
                IO.println("0   Print this help summary");
                IO.println("1   Add a normal score");
                IO.println("2   Print the student's percentage");
                IO.println("3   Quit program");
            } else if(option == 1) {
                IO.print("Score? ");
                int score = IO.readInt();
                IO.print("Possible? ");
                int possible = IO.readInt();

                stud.addScore(score, possible);
            } else if(option == 2) {
                IO.print("Total is ");
                IO.print(100.0 * stud.getGrade());
                IO.println("%\n");
            } else if(option == 3) {
                break;
            } else {
                IO.println("Invalid option. Press 'h' for help.");
            }
        }
    }
}

An example run of this program:

Option (0 for help)? 1
Score? 23
Possible? 30
Option (0 for help)? 1
Score? 7
Possible? 7
Option (0 for help)? 2
Total is 75.0%
Option (0 for help)? 3