ArraysTopFor loops

For loops

We have spent quite a bit of time on the topic of recursion. We have discussed recursion as a mechanism for implementing repetition. We have also examined recursive data structures, ending with the topic of singly linked lists, which allow us to represent a collection of data in a linear fashion.

This week we continue exploring the themes of repetition and data collections. We begin by looking at a new looping construct. We'll then turn to the topic of arrays.

Recognizing patterns

A very important part of programming is recognizing patterns so that you can reuse them. Here is a pattern that you have seen many times this semester:
    public void onMousePress(Location point) {
        lastPoint = point;
    }
    
    public void onMouseDrag(Location point) {
        if (dragging) {
              // do something with point and lastPoint
            lastPoint = point;
        }
    }

We used this pattern when we were dragging things around (in that case the "do something" involved moving an object by the difference between point and lastPoint) and when we were scribbling on the screen (in that case we drew a line between lastPoint and point).

Here is a very similar type of pattern:

    lastTime = getTime();
    while (...)
    {
       ...
       elapsedTime = lastTime - getTime();
       ...move(elapsedTime*xspeed, elapsedTime*yspeed);
       ...
       lastTime = getTime();
       pause(...);
    }
}

This pattern showed up with active objects when we wanted to make sure that we moved each item proportional to the amount of time since it was last moved.

Of course, these two patterns are really the same. They involve remembering a value before a change happens, then using both the new and old values after the change happens, and then remembering the current value so it can be used after the next change occurs.

If you learn to recognize these patterns in code, you will be able to apply them to your own programming. The idea is that most of the time, you can use well-known techniques to accomplish whatever it is you want to do. That way you can save your time and energy for the new hard problems when you encounter them. In virtually all of our lab assignments, you will find the first part of the assignment is to see if you can apply a pattern that we have taught, while the second part attempts to stretch your creativity and problem-solving skills to accomplish something newer and more interesting.

Counting and For loops

For loops in Java capture a very common pattern found in while loops. While loops are often used for counting. Let's look at some examples. In our falling snow example we had code something like:

    int snowCount = 0;         // # of snowflakes generated so far
        
    while (snowCount < 150 )
    {
        new FallingSnow( canvas, snowPic, 
                         snowGen.nextValue(),   // x coordinate
                         snowGen.nextValue()*2/aScreenWidth+2, // y speed
                         screenHeight);
        pause(900);    
        snowCount++;

    }

Recall that writing snowCount++ is the same as writing snowCount = snowCount + 1. If we carefully examine the loop in the falling snow example above, we can see that it has the following structure:

    int counter = 0;
    while (counter < stopVal)
    {
        // do stuff
        counter++;     
    }

It turns out that we can use a different construct that localizes the code dealing with counting so that it is easier to understand. This construct is called a for loop. You would use it for counting by saying the following:

    for (int counter = 0; counter < stopVal; counter++)
    {
       // do stuff - but omit counter++ at end
    }

The code in the parentheses consists of 3 parts; it is not just a condition as in if or while statements. The parts are separated by semicolons. The first part is executed once when we first reach the for loop. It is used to declare and initialize the counter. The second part is a condition, just as in while statements. It is evaluated before we enter the loop and before each subsequent iteration of the loop. It defines the stopping condition for the loop, comparing the counter to the upper limit. The third part performs an update. It is executed at the end of each iteration of the for loop, just before testing the condition again. It is used to update the counter.

How would we rewrite the falling snow example to use a for loop?

    for (int snowCount = 0; snowCount < 150; snowCount++)
        {
            new FallingSnow( canvas, snowPic, 
                         snowGen.nextValue(),   // x coordinate
                         snowGen.nextValue()*2/aScreenWidth+2, // y speed
                         screenHeight);            

            pause( 900);    
        }
Essentially we have taken three lines from the above while loop version and combined them into one line of the for loop version. Because we included the declaration of the counter inside the loop (see int counter), it is only available inside the loop. If you try to use it outside of the loop, Java will claim to have never heard of a variable with that name.

Notice how the for localizes the use of the counter. This has two benefits. First, it simplifies the body of the loop so that it is somewhat easier to understand the body. More importantly, it becomes evident, in one line of code, that this is a counting loop.

Calculating Interest Example

Let's see another example of a for loop. Suppose that we wanted to determine how much money we would have if we invested it for 10 years, gaining 5% interest each year (compounded annually). Suppose amount represents the amount of money we have invested. After one year, we would have:

    amount + amount * 5/100.0;

How would we use a for loop to repeat this computation 10 times?

    private double amount = startInvestment;  // value of investment
    private static final int RATE = 5;   // interest rate as percent
    private static final int YEARS = 10;      // number of years for investment
    
    for (int yearNum = 1; yearNum <= YEARS; yearNum++)
    {
        amount = amount + amount * RATE/100.0;
    }

Exercise

Suppose we divided by 100 rather than 100.0 above. Would it make a difference in the final answer? The answer is no, it would not make a difference, even though 5/100 equals 0 with integer division. The thing that saves us is that arithmetic is done from left to right for operations of the same precedence. Thus first amount * RATE is calculated, giving a double (mixed mode arithmetic always results in doubles). That double would then be divided by 100, yielding another double. However if we wrote the expression instead as amount * (RATE/100), so that the division is done first, then the answer would be 0 if RATE < 100. Can you guess why we divided by 100.0 above? It was to make sure that it did a real number divide rather than integer divide. Typing an extra ".0" was a small price to pay for piece of mind!

Below is a demo of an interest program that computes interest for a single rate:



It is slightly more general than the example above because it allows the user to enter the desired interest rate, starting amount, and number of years using AWT TextFields.

Now, suppose we are considering different types of investments that have different interest rates associated with them. We want to determine what effect the different interest rates would have in the long run. Now the user can enter a lower and upper bound on interest rates rather than a single interest rate. Note that we now get multiple output statements, one for each interest rate.

Let's consider a more general version of this interest program that codes in all the values that our demo allows the user to provide. In this version, the range of interest rates goes from 2% to 12%, with values at each percentage. In the code above,we computed the interest for a single interest rate. How can we repeat it for multiple interest rates? We can create a nested for loop. The inner for loop increments through the years as above. The outer for loop steps through the interest rates:

    private double amount = startInvestment;  // value of investment

    private static final int START_RATE = 2;   // interest rates
    private static final int END_RATE = 12;

    private static final int YEARS = 10;      // number of years for investment

    for (int rate = START_RATE; rate <= END_RATE; rate++)
    {
        amount = startInvestment;
        for (int yearNum = 1; yearNum <= YEARS; yearNum++)
        {
            amount = amount + amount * rate/100.0;
        }
        System.out.println("At "+rate+"%, the amount is: "+amount);
    }

Note that the inner for loop is executed all the way through each time the outer loop goes through one iteration. Thus, when initially rate is START_RATE, the inner for loop is executed a total of years times in order to calculate an amount, which is then printed. Then rate is incremented by 1 and the inner loop is executed years times again, and the new value is printed. Notice that we would get the wrong answer if we did not reset amount to startInvestment each time we begin the outer loop.

Note that rate does not start at 0 or 1, like all our previous counters. Instead it starts at 2. This is fine. We can initialize our "counter" any way that we like in the first part of the for loop.

Knitting a scarf: another example of nested For loops

The previous example shows us incrementing on each loop, but we are not really counting since we didn't start at 0 or 1. As an example of nested for loops where we really are counting, recall the program we wrote earlier in the semester that draws a scarf:

 // draws a scarf with upper left at point of click
    public void onMouseClick( Location point )
    {
        double x = point.getX();
        double y = point.getY();    // x and y positions of the next ``stitch''
        
        int numRows = 0;
        int numCols = 0;
        
        while (numRows < LENGTH) {
          while (numCols < WIDTH) {
              new FramedOval(x, y, DIAMETER, DIAMETER, 
                             canvas).setColor(Color.green);
              x = x + X_DISP;
              numCols = numCols + 1;
          }
            
          x = point.getX();
          y = y + Y_DISP;
          numCols = 0;
          numRows = numRows + 1;
        }
    }

This can be rewritten, replacing the while loops with for loops as follows:

 // draws a scarf with upper left at point of click
    public void onMouseClick( Location point )
    {
        double x = point.getX();
        double y = point.getY();    // x and y positions of the next ``stitch''
        
        for (int numRows = 0; numRows < LENGTH; numRows++) {
          for (int numCols = 0; numCols < WIDTH; numCols++) {
              new FramedOval(x, y, DIAMETER, DIAMETER, 
                             canvas).setColor(Color.green);
              x = x + X_DISP;
          }
            
          x = point.getX();
          y = y + Y_DISP;
        }
    }

Other variations

The interest example showed us incrementing on each loop, but we didn't start at 0 or 1.

Other variations are possible. We could count down instead of up:

    for (int countdown = 10; countdown >= 1; countdown--)
    {
        System.out.println(countdown);
    }
    System.out.println ("Blast off!");

We could increment by a value other than 1. For example, we could have kept our interest rate as a double and then incremented by .01.

    private double amount = startInvestment;  // value of investment

    private static final double START_RATE = .02;   // interest rates
    private static final double END_RATE = .12;
    private static final double RATE_INCREMENT = .01;

    private static final int YEARS = 10;      // number of years for investment

    for (double rate = START_RATE; rate <= END_RATE; rate = rate + RATE_INCREMENT)
    {
        amount = startInvestment;
        for (int yearNum = 1; yearNum <= YEARS; yearNum++)
        {
            amount = amount + amount * rate;
        }
        System.out.println("At "+ (rate * 100) +"%, the amount is: "+amount);
    }

Warning: Note that you need to be very careful when testing for equality of floating point numbers. Due to roundoff errors, your numbers might not be what you expect.

Summary of for loops

The general structure of a for statement is the following:

    for ( <initialization>; <condition>; <update>) 
    {
      <code to repeat>
    }

When should you use a for loop instead of a while loop:


ArraysTopFor loops