![]() | ![]() | ![]() | 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.
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.
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.
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; }
Below is a demo of an interest program that computes interest for a single rate:
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.
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; } }
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.
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:
![]() | ![]() | ![]() | For loops |