Graphic by Keith Ohlfs
CS111, Wellesley College, Spring 2000

Forms of Iteration

[CS111 Home Page] [Syllabus] [Lecture Notes] [Assignments] [Labs] [Programs] [Documentation] [Software Installation] [FAQ] [CS Dept.] [CWIS]

We've studied recursion which allows us to build complicated patterns and solve problems by using the same method over and over again. Iteration is another way to express "do something many times". Most problems can be solved via both recursion and iteration, but one form may be much easier to use than the other. We will study three forms of iteration: tail-recursion, while loops, and for loops.

We will use the task of reversing a list as an example to illustrate how different forms of iteration are related to each other and to recursion. A recursive implementation of reverse is given below.

public static IntList reverse (IntList L) {
  if (isEmpty(L)) {
    return empty();
  } else {
    return postpend(reverse(tail(L)), head(L));
  }
}

public static IntList postpend (IntList L, int n) {
  if (isEmpty(L)) {
    return prepend(n, empty());
  } else {
    return prepend(head(L), postpend(tail(L), n));
  }
}
The recursive method reverse above not only calls itself, but it also must do something to the result it gets from calling itself. The answer from the recursive call to reverse is used as a parameter to postpend. We call postpend a pending operation in the recursion. Iteration, on the other hand, does not have pending operations. Each pass through the iteration is a complete set of calculations. To achieve this, extra variables are usually necessary to keep track of the state of things during the iteration and to hold the partial state of the answer we are putting together.

Iteration Tables

An iteration involves state variables which keep track of the state of each iteration variable for each pass through the iteration. An iteration table is a way to visualize the states of the iteration. The columns of the iteration table are the state variables involved in the iteration and the rows are the values of the state varibles in each invocation of the iteration.

To solve the problem of reversing a list iteratively, we need a different reverse strategy. An alternative technique for reversing a list is to follow the strategy one would use in reversing a pile of cards: form a new pile by iteratively removing the top card of the original pile and putting it on the new pile. When there are no more cards in the original pile, the new pile contains the cards in reverse order from the original pile. To implement this iterative strategy, we need to keep track of the list we are reversing, and the result we are building. Given a list L1=[1,2,3,4], the iteration table for reverse(L1) would be

list result
[1,2,3,4] []
[2,3,4] [1]
[3,4] [2,1]
[4] [3,2,1]
[] [4,3,2,1]

Tail-Recursion

Tail recursion is an iteration which involves recursion, but no pending operations. The relationship between an iteration table and a tail recursion implementation is as follows: If we are given a tail-recursive implementation of a method, we can derive the iteration table by creating a column for each parameter in the tail-recursive method.

For the tail-recursive implementation of reverse, we see that we need an auxiliary method (which we call reverseTail) because reverse has one parameter list but the iteration table has two state variables (list and result). The implementation with comments is given below.

// main method
public static IntList reverse(IntList list) {
  // returns the result of the auxiliary method
  // the parameters of the auxiliary method are the
  // values in the first column of the iteration table
  return reverseTail(list, empty());
}

// tail-recursive auxiliary method
// notice there is one parameter for each state variable
// (column) of the iteration table
public static IntList reverseTail(IntList list, IntList result) {
  if (isEmpty(list)) { // base case: when to stop
    return result;     // this is the state variable which holds the answer
  } else {             // general case: do a recursive call
    // notice there are no pending operations!
    // the next value of list is tail(list)
    // the next value of result is prepend(head(list), result)
    return reverseTail(tail(list), prepend(head(list), result));
  }
}

while loops

A while loop is a form of iteration which does not involve recursion. Instead, the syntax creates a loop which is a body of code which is repeated over and over again while some condition is true. The loop is terminated when the specified condition is false. The formal syntax for a Java while loop is
while (continuation-condition) {
  loop body statements
}
The relationship between a while loop and the iteration table and tail-recursive implementation is as follows: If we are given a while loop implementation of a method, we can derive the iteration table by creating a column for each parameter in the method and for each state variable which is initialized outside the loop and updated inside the loop. Variables which are initialized outside the loop and not modified within the loop are not needed in the iteration table.

For the while loop implementation of reverse, we see that we need to initialize one state variable result at the beginning of the method because reverseWhile has one parameter list but the iteration table has two state variables (list and result). The implementation with comments is given below.

public static IntList reverseWhile(IntList list) {
  IntList result = empty(); // initialize state variable

  while (!isEmpty(list)) {  // continuation condition is opposite of base case
    // update state variables in body of loop
    // must update result before list because
    // it depends on the original value of list
    result = prepend(head(list), result);
    list = tail(list);
  } // end of loop

  return result; // answer is returned when loop is finished
}

for loops

A for loop is a form of iteration which is just a while loop written in a more compact syntax. In computer science, we call this phenomenon syntactic sugar. So, a for loop is syntactic sugar for a while loop. Every for loop can be written as a while loop, and vice versa, but it may not always be easy to do so. for loops are generally used with arrays which we will be studying in the near future. Their syntax is useful in situations in which there is a counter which is incremented or decremented with each pass through the iteration. The formal syntax for a Java for loop is
for (counter-initialization; continuation-condition; counter-update) {
  loop body statements
}
The relationship between a for loop and while loop implementation is as follows: If we are given a for loop implementation of a method, we can derive the iteration table by creating a column for each parameter in the method, for each counter, and for each state variable which is initialized outside the loop and updated inside the loop. Variables which are initialized outside the loop and not modified within the loop are not needed in the iteration table.

For the for loop implementation of reverse, determining what the counter will be is a bit tricky since there aren't any numbers which are incrementing or decrementing. Instead, we pick the IntList list to be our counter because it is shrinking with each pass through the iteration. Since list is a parameter of our method, it doesn't need to be initialized. Therefore, there will be nothing in the counter-initialization part of our for loop. The implementation with comments is given below.

public static IntList reverseFor(IntList list) {
  IntList result = empty(); // initialize state variable

  // no counter-initialization
  // loop continues while list is not empty
  // list becomes tail(list) with each pass through iteration
  for ( ; !isEmpty(list); list=tail(list)) {
    // update state variables in body of loop
    result = prepend(head(list), result);
  } // end of loop

  return result; // answer is returned when loop is finished
}