The Repetition Control Structure (a.k.a. Looping)


This document introduces fundamental concepts related to the design of repetitious processes. Readers of this document may benefit from a review of Flowcharting Symbols and Logical Control Structures. Readers who have difficulty rendering flowcharts are provided with links to alternative text-based outlines prior to each flowchart below. For specific examples of loop algorithms and C Language code, view the web pages entitled Example of a Counting Loop (Repetition Structure) and Analysis of an accumulation using the repetition structure.


Repetition is the act of repeating one or more steps in a process. It involves a branching backwards away from the normal sequence of steps towards an earlier step. The branching decision is based on a condition (relationship between the values of known data) at the time that the branching test is performed. The condition is normally based on the value of a single variable known as the control variable. The step or steps to be repeated are referred to as the loop body. Each execution of those steps is referred to as a pass or iteration. The step at which the body starts is known as the loop entrance and the step at which the test to branch back fails (causing repetition to cease) is known as the loop exit. The illustration below shows two variations of control structure commonly used by analysts when designing repetitive processes. Readers who have difficulty rendering flowcharts can read the alternative [text-based outlines] for these examples instead.

Pair of Leading and Trailing Decision Flowcharts showing Repetition Structure

The major issues involved in loop design are: structure, method of control, and boundary conditions. These will be discussed in detail in the following paragraphs, but are listed briefly below to introduce them.


Loop Design Issues - in Detail

STRUCTURAL ISSUES

When programmers want to repeat a step within a program a known number of times, some additional steps will be required to implement the repetition. A test must be added to determine if a repetition should take place or end. Other additional steps might also be required if the repetition is dependent on controlling a counter of events (passes). The step at which the loop starts is called its entrance and the last step performed before completion is called its exit. Programmers have learned over the years that the conditional test that controls the exit from a loop should be placed either immediately following the entrance to the loop or following all steps in its body, but never in the middle of the body. Loops that have their tests in the middle are often error prone and difficult to debug. Loops with their tests at the entrance are said to use a pretest (leading) decision and those with their tests after their entire body use a posttest (trailing) decision. Programming languages offer a variety of statements to implement the two repetitive control structures. Beware that many of the keywords used to do this are not handled in the same manner by each language. Some of the more popular keywords are: while, for, do, and until. Some times these are used in combination. Students are cautioned to avoid using such keywords to describe the structure of a loop because each language uses these words differently. Authors of textbooks about the C++ Language often refer to leading decision loops as "while-do" loops, and refer to trailin decision loops as "do-while" loops. Others call trailing decision loops "repeat-until" loops. Do not fall into this bad habit of using language keywords to discuss logical control structure. Stick with the terms "leading" and "trailing" decision. They are not language-dependent.

Many loops can be written using either the pretest or posttest decision structure without any detrimental effect on the results. But (for example) if I was writing a loop to repeatedly display lines on a report that were based on complex calculations, I would probably chose to use the posttest decision structure. The trailing position of the test would guarantee that I would see at least one line of output even if the conditions forced an exit after the first pass. If my program's calculations were producing flawed results and I chose (unwisely) to use a pretest decision structure, the test might force an exit from the loop before I (or the user) had a chance to see any of the values causing the trouble. In a different program involving erasure of data or activation of dangerous equipment, I might want to guarantee that a test was always performed prior to any action being done in the body of a loop (because its action was hazardous). In that case, I would chose to use the pretest decision structure.

Novice programmers often develop loops that perform their test in the middle of the body as shown in the illustration below. This approach appears to be quite logical and is often more efficient that either the leading or trailing decision approaches. And yet, many modern programming languages have no command to implement this approach because it has been determined to be more likely to contain flaws and difficult to debug. Any loop design that tests in the middle can be redesigned to test at the entrance or exit, but will usually require the addition of some extra steps (overhead) to accomplish the its objective. The benefit is that almost all programming languages have commands to implement both the leading and trailing decision structures.

Flowchart of a middle test in a repetition structure

CONTROL ISSUES

The choice of control method is dictated by whether the decision to perform repetitious steps is supposed to be controlled by the user or by the programmer. If the decision to repeat is to be based on a value entered by the user, then the control method is sentinel (external) control. If the decision to repeat is to be based on a value established and controlled solely by the programmer without any input by the user, then the control method is counting (internal) control. In some loops, the decision condition is not as simple. It is based on more than one factor; one user-defined, another programmer-defined. Situations like that use hybrid (combined) control and involve more complex conditional expressions.

It is important to draw a distinction between loops that involve counting as part of their purpose and other loops that use counting as their method of control. Just because a loop involves counting, does not guarantee that its control method is based on the value of the counter. So not all loops that count are "counting controlled" loops. Sentinel loops might also do some counting.

Consider the following illustration that shows two different structural approaches that could be used in designing a loop that requires the counting method of control employing a counter variable labeled C. The objective of the loop is to display the word "Hello" five times on separate lines. In this example, the counter C has nothing to do with the action to be repeated (display of the word) except to control how many times the action will take place. Some loops do contain bodies that involve the counter. This would be the case in this example if the object of the body was to display the value of the counter instead of the word "Hello", in which case the output would be a column of numerals (1 through 5). (For an example of such a loop see the web page Example of a Counting Loop (Repetition Structure).

The flowchart below on the left shows the original process using the posttest decision structure. The flowchart below on the right shows the original process using the pretest decision structure. Readers who have difficulty rendering flowcharts can read the alternative [text-based outlines] for these examples instead. As stated in the section above about structure, the choice of one looping structure over another often has no effect on the ability of the structure to accomplish its objectives. Both of the structures below will work equally well to accomplish the task.

Pair of Leading and Trailing Decision Flowcharts showing loops that display the word "Hello" 5 times in a column

The comments included in the flowcharts above relate to the fact that all counting controlled loops contain (at least) four basic elements. These are:

These elements do not always occur in the order shown above, but they are always present (in counting controlled loops).

The C++ source code for the trailing decision approach shown in the flowchart above on the left would be

#include <iostream>  /* Standard Input/Output header file */
using namespace std;

int C;  /* Counter */

int main (void)
{
  C = 1;                   /* Initialize counter to start at one */
  do
    {                      /* Start a pass through the loop */
      cout << "Hello\n";   /* This step is the "body" of the loop */
      C = C + 1;           /* Increment C by one */
    }                      /* End the pass through the loop */
  while (C<=5);            /* Test for repetition/exit AFTER the body */
  return 0;                /* Return zero error code to parent process */
}

Note the need for the braces { } surrounding the body and increment. The do statement must contain the actions to be repeated. It (like most branching oriented statements in C++) can perform only one statement. So if we need to have more than one performed, they need to be enclosed in braces to have C++ treat them as a compound statement. The while statement performs the test of the parenthesized condition. If it is true control branches back to the do statement. If not, or not, control continues in sequence to the next statement following the while statement.

The C++ source code for the leading decision approach shown in the flowchart above on the right would be

#include <iostream>  /* Standard Input/Output header file */
using namespace std;

int C;  /* Counter */

int main (void)
{
  C = 1;                  /* Initialize counter to start at one */
  while (C<=5)            /* Test for repetition/exit BEFORE the body */
    {                     /* Start a pass through the loop */
      cout << "Hello\n";  /* This step is the "body" of the loop */
      C = C + 1;          /* Increment C by one */
    }                     /* End the pass through the loop */
  return 0;               /* Return zero error code to parent process */
}

We also need braces { } surrounding the body and increment here. The while statement performs the test and if the parenthesized condition is true, executes the single (or braced) statement(s) beyond the while statement. Then control branches back to the while statement to perform the test again. If the condition is false, control branches ahead (in the code) to the next statement following the single (or braced) statement(s) beyond the while statement (in this example: return 0).

BOUNDARY CONDITION ISSUES

In the loops above, the value of the control variable is not be directly involved in the steps being repeated. The counter must step through five values, but the actual values are not intimately involved in the process being repeated. In such cases, any values for the counter would be acceptable as long as the desired quantity of repetitious passes occurs. The counter could run from 1 to 5, 11 to 15, or down from 5 to 1, and produce the same five events. The counter could also step using increments other than 1. For example, the following automatic looping statement (although a bit odd) would accomplish the goal of the "Hello" loop above:

	float N;  for ( N=1.1; N<=1.5; N=N+0.1 ) cout << "Hello\n";

(If you are not familiar with automatic "for loops", look in your textbook in chapter 5.) Often when designing loops, the exit value of the control variable is important to us. For example, consider the following loop:

	int C;  C = 1; while (C<=5) { cout << "Hello\n"; C=C+1; }

In the loop above, C++ would have an exit value of 6, because it had to exceed 5 for the test to produce a false result and allow an exit from the loop. However, the last value that user would see would be 5. The loop design could be altered to guarantee that the last value displayed was the exit value. One such design would be:

	int C;  C = 0; while (C<5) { N=N+1; cout << "Hello\n"; }

Note that:

  1. the initial value was altered
  2. the test condition had to be slightly altered, and
  3. the order of the steps in the braced pass changed

Designers should always consider the entrance and exit values of all variables that are to be effected by a loop.

PATH: Instructional Server> COP 2000> Examples> Repetition>