Loops

Some motivation for why loops are needed

Suppose you needed to write a program that would print a string (e.g., "Howdy Bob!") to the screen some very large number of times, say a thousand. We could, of course, (although it would be terribly inefficient and cumbersome) simply type:

System.out.println("Howdy Bob!"); 
System.out.println("Howdy Bob!"); 
System.out.println("Howdy Bob!"); 
           .
 (repeat this 1000 times)
           .
System.out.println("Howdy Bob!");

Clearly, this is not a pleasant solution. The situation can be worse, however.

Consider the case where we need to write a program that generates quiz problems for first graders to help them practice subtraction. We would like to give them unlimited opportunities to "guess again" if they enter a wrong answer to any given problem.

It is conceivable, however, that a child could get the answer wrong once, twice, fifty, or a thousand or more times -- but we don't know which! When it comes to writing the code to get the input, how many statements that scan for input must be included?

Fortunately, Java provides us with "looping structures" which allow us to execute a statement or group of statements over and over again, either some fixed number of times, or until some specified condition is met.

The "while-loop"

The simplest looping structure in Java is the "while-loop". Here is the syntax:

while (someBooleanCondition) {
   statement(s);
}

The while loop will continue to repeatedly execute the statements in the block following the boolean condition as long as the the boolean condition is true. As soon as the boolean condition is false, the loop stops, and the program proceeds normally.

So for example,

int i = 1;                                 //initialize i as 1
while (i <= 4) {                           //keep printing message and 
  System.out.println(i + " Howdy Bob!");   //increasing i by one each 
  i++;                                     //time until i exceeds 4
}
System.out.println("Loop stopped.");       //let the user know the 
                                           //loop stopped

will produce the following output:

1 Howdy Bob!
2 Howdy Bob!
3 Howdy Bob!
4 Howdy Bob!
Loop stopped.

Ending a loop with a sentinal value

Frequently, the number of times a loop is executed is not predetermined. This is especially true when trying to get data either directly from the user or, as we shall see later, from external files. In these cases we will need to scan for input values until some special value is read that signifies the end of the data has been reached. This special value is called a sentinal value.

As an example, suppose you wanted to write a program that reads and calculates the sum of an unspecified number of integers the user might input. Under the assumption that the user will never be entering a zero as one of the numbers to be added, we can use this value as our sentinal value, and instruct the user to simply input a zero when he is done entering all of the values to be summed. The following code implements this idea:

int currentSum = 0;                    //we haven't read any numbers yet

int inputNum = myScanner.nextInt();    //get first number from user
while (inputNum != 0) {                //as long as user didn't type zero:
   currentSum = currentSum + inputNum;  //add the input to the current sum
   inputNum = myScanner.nextInt();      //and ask user for another number
}

System.out.println("The sum is " + currentSum);  //print final sum

The "do-while-loop"

Sometimes, like in the last example, we know that we want the body of the loop to be executed at least once. (In the example above, we need to ask the user to enter a value at least once -- if only for him to tell us he has no numbers to add.) This is where a "do-while-loop" might be appropriate. Here is the syntax:

do {
    statement(s);
} while (someBooleanCondition);

In this looping structure, the statement(s) in the body of the "do-while-loop" automatically get executed once, and then they continue to be executed as long as the boolean condition following the word "while" remains true. As soon as this boolean condition becomes false, the loop terminates, and the rest of the program proceeds.

So for example, the following code:

int inputNum;
int currentSum = 0;

do {
   inputNum = myScanner.nextInt();
   currentSum = currentSum + inputNum;
} while (inputNum != 0);

System.out.println("The sum is " + currentSum);

will also add up the numbers the user enters until the user enters a zero, and then print out the final sum. However, we don't need the extra "myScanner.nextInt()" statement, since the loop body gets executed at least once.

The "for-loop"

Many times, we do know how often some statement or group of statements should be executed. Consider again the following code:

int i = 1;                                 //initialize i as 1
while (i <= 4) {                           //keep printing message and 
  System.out.println(i + " Howdy Bob!");   //increasing i by one each 
  i++;                                     //time until i exceeds 4
}

which produces the following output:

1 Howdy Bob!
2 Howdy Bob!
3 Howdy Bob!
4 Howdy Bob!

Notice how the variable "i" plays the role of a "counter", keeping track of how often we have done the task of printing "Howdy Bob!" to the console.

Further, notice how

  1. before the loop is entered, the value of "i" is initialized to 1;
  2. we check to see if the looping should continue at the start of each pass, by looking at the condition "(i <= 4)"; and
  3. at the end of each pass through the loop, the value of "i" is incremented by one

Such constructions happen so frequently in Java, that we have a special loop designed for just these situation. It is known as the "for-loop". Here is the syntax:

for (initial-action; continuation-condition; action-after-each-iteration) {
   statement(s);
}

To rewrite the code above (which prints "Howdy Bob" 4 times, just like before), using a "for-loop", we would have:

for (int i = 1; i <= 4; i++) {
   System.out.println(i + " Howdy Bob!");
}

Notice how much cleaner this is!

Less-often seen constructions of the "for-loop"

It is possible for the initial-action and the action-after-each-iteration in a for loop can be a list of zero or more comma-separated expressions. So, for example, the following is legitimate:

for (int i = 0, j = 0; (i + j < 10); i++, j++) {
   //do something
}

Also, if the continuation-condition in a "for-loop" is omitted, it is implicitly true. In other words:

for ( ; ; ) {
   //do something
}

IS THE SAME AS...

while (true) {
   //do something
}

Notice, both of these loops will do the "something" an infinite number of times (This is dangerous!), unless that "something" contains a statement that let's one "break-out" of the loop.

A common mistake

The "for-loop" will repeatedly execute the first statement or block statement that occurs after the specification of the initial-action, continuation-condition, and action-after-each-iteration.

A common error is to put a semicolon after the aforementioned specification. For example,

int i; 

for (i = 0; i < 10; i++);
{
   System.out.println(i);
}

outputs only a single

10

The semicolon (shown in red) acts as a "do-nothing" statement, and "nothing" is exactly what is repeatedly executed by the "for-loop" -- instead of the println statement. The value of i is affected (hence, the output of 10), but clearly (given the suggestion the indentation makes) the outcome is not what was intended.

Floating-point numbers and loops

Remember that floating-point numbers (like doubles) are represented only approximately in the computer. You should avoid (or at least, be REALLY careful when) using floating point number in a loop's continuation-condition. Something like the following is doomed to not work like you think it should:

double sum = 0.0;
for (double d = 0.0; d != 1.0; d = d + 0.01) {  //problem!
  sum = sum + d;
}

One might think that at some point d must equal 1.0, since we started at 0.0 and added 0.01 to it repeatedly.

Unfortunately, given the approximate nature of doubles' representation, d may only eventually equal something close to 1.0 (like 1.00000000000042). Consequently, the continuation-condition never fails, and the loop just keeps on chugging away until the numbers get so huge the program chokes on them.

Moral of the story: Don't use != or == to compare floating-point numbers!

If you must compare floating-point numbers, test whether or not they are "close enough". That is to say, is the distance between these two numbers "small enough" (i.e., less than some predetermined "really small" size, which we might call "EPSILON", to borrow an idea from calculus).

To say the same thing, mathematically

$$x \approx y \quad \textrm{if} \quad |x-y| \le \epsilon \quad \textrm{, where $\epsilon$ is some small number like $10^{-14}$}$$

Which loop to use?

  • "while-loops" and "for-loops" test the loop continuation condition before the loop body occurs; "do-while-loops" test this condition after the loop body occurs.
  • The three loop structures in Java can be used almost interchangeably most of the time, but be aware: other programming languages don't offer this same flexibility. To facilitate your learning other languages, follow the guidelines below:
    • A "for-loop" should be used if the number of repetitions is known ahead of time, as for example, when you need to print a message 100 times.
    • A "while-loop" can be used if the number of repetitions is not known, as in the case of reading numbers until the input is 0.
    • A "do-while-loop" can be used to replace a "while-loop" if the loop body has to be executed once before testing the continuation condition.

How to implement loops

  1. List the work that needs to be done in every step of the loop body
  2. Find out how often the loop i repeated and how we can determine if the loop is finished -- this will help you pick between using a "while-loop" or a "for-loop"
  3. Implement the loop by putting the operations from step 1 into the loop body.
  4. Double-check variable initializations and updates, and check for infinite loops and "off-by-one" errors.

Using "break" and "continue"

  • break -- ends the innermost loop that contains it. It "breaks out" of the loop. (It can be used in loops and switch statements.)
  • continue -- ends the current iteration and "continues" onto the next iteration

These two commands are normally used with an "if-statement" to break or continue the loop under some given condition.

Interestingly, one can mathematically prove that any task you want to accomplish with a program can always be accomplished with a program that doesn't use "break" or "continue" -- but their usage can greatly shorten your code!