COSC 1315

Programming Fundamentals

Counter Loops, Nested Loops, and Sentinel Loops

Revised:  February 4, 2007
By Richard G. Baldwin

File:  Pf00184.htm
Practice Text


Preface

This lesson was written specifically for the benefit of my students in COSC 1315, Fundamentals of Programming.  The lesson was written under the assumption that those students have no prior programming knowledge when they enroll in the course.

Another browser window

I recommend that you open another copy of this document in a separate browser window so that you can view the code and the discussion of that code at the same time.

Discussion

In this lesson, I will present and explain three programs, which, in combination illustrate the following kinds of specialty loops:

Counter, nested, and for entry-condition loops

The first program named NestedCounterLoop01 will use a for loop structure to illustrate both counter loops and nested loops.

Sentinel entry-condition loops

The second program named SentinelLoop01 will use a while loop structure to illustrate sentinel loops.

Sentinel exit-condition loops

The third program named SentinelLoop02 will use a do-while loop structure to illustrate both sentinel loops and exit-condition loops.

Object-oriented syntax

All three programs use the basic object-oriented syntax template that I introduced in the lesson entitled Basic OOP program structure.

All of the new code in each program is contained in an instance function named doSomething.  Therefore, I will discuss the code in the doSomething function for each program and refer you to the Complete Program Listings for the remainder of the code.

Sample Programs

The program named NestedCounterLoop01

This program uses an entry-condition for loop structure to illustrate both counter loops and nested loops.

What is a counter loop?

The name counter loop results from the fact that a loop of this type runs an incremental counter and continues looping until the value of the counter reaches a pre-specified limit.

What is a nested loop?

The name nested loop results from the fact that it consists of two or more loop structures nested inside one another.

This program consists of one for loop nested inside another for loop.  Each of the for loops run an incremental counter and use the value of the counter to determine when to stop looping.

The function named doSomething

A complete listing of this program can be viewed in Listing 9 near the end of the lesson.  The beginning of the function named doSomething is shown in Listing 1.

  void doSomething(){
    int rowLim = 3;
    int colLim = 5;

    for(int rCnt = 0; 
        rCnt < rowLim; 
        rCnt = rCnt + 1){

Listing 1

New material in this lesson

Insofar as this series of tutorial lessons is concerned, there are two things that are new about this program:

Thus, the program consists of an outer for loop and a nested inner for loop.  The beginning of the outer for loop is shown in Listing 1.

A for loop header

The last three lines in Listing 1 shows the header for the for loop.

(All three lines are typically placed on the same line.  I separated them onto three lines in Listing 1 to make the different parts stand out.)

The loop header is followed by the loop body, which begins with the curly brace shown at the right end of the last line in Listing 1.

Three clauses in the header

The header for a for loop contains three clauses surrounded by parentheses and separated by semicolons.  The three clauses appear in the following order from left to right (top to bottom in Listing 1):

The initialization clause

The optional initialization clause is executed once and only once before any other element of the for loop is executed.  Control then passes to the conditional clause.  As the name implies, this clause is used to initialize something.  In this program, it is used to declare and to initialize the value of a counter named rCnt to zero.

(I could have declared and initialized the counter before entering the loop, but didn't elect to do so.  Had I done so, I could have left this clause blank, but the semicolon would still have been required.)

The conditional clause

The conditional clause behaves pretty much the same way as the conditional expression in a while loop or an if statement.

The expression in the conditional clause is evaluated before the execution of each iteration of the code in the body of the loop.  (Like the while loop, the for loop is an entry-condition loop.)

If the expression in the conditional clause evaluates to true, the code in the body of the loop is executed once.  Then the update clause is evaluated and the test in the conditional clause is performed again.

Thus, the code in the body of the loop continues to be executed for as long as the conditional clause evaluates to true(Hence the name of the for loop.)  When the conditional clause evaluates to false, the body of the for loop is skipped and control passes out of the loop.

The update clause

Regardless of the fact that the optional update clause is physically located in the loop header at the top of the loop, it is executed as if it were positioned at the end of the body of the loop.  In other words, once during each iteration of the loop body, after all of the code in the body of the loop has executed, the update clause is evaluated.

In this program, the update clause is used to increment the counter eventually causing the conditional clause to evaluate to false, causing the loop to terminate.

The update clause can be left blank, but even in that case, the semicolon is required.  Also in that case, some other process must be used to eventually cause the loop to terminate.

Behavior of the outer loop

Returning now to the code in Listing 1.  The purpose of this program is to compute and to display a set of values in a row-column matrix format as shown in Figure 1.

0 0 0 0 0
0 1 2 3 4
0 2 4 6 8
Figure 1

Number of rows and columns

Listing 1 declares and initializes a pair of variables named rowLim and colLim to values of 3 and 5 respectively.  These variables will be used to cause the matrix that is displayed to have 3 rows and 5 columns.

A row counter named rCnt

The initialization clause in the outer for loop header in Listing 1 declares a row counter named rCnt and initializes its value to 0.  At the end of each iteration of the body of the outer loop, the update clause increments the row counter by a value of 1 by adding 1 to the current value of the row counter.

The conditional test

According to the conditional clause, the outer for loop will continue to iterate for as long as the value of the row counter is less than the value of rowLim

Thus, the outer for loop in this program controls the iteration on the rows shown in Figure 1.  All of the code in the body of the loop, which includes an inner for loop, will be executed during each iteration of the outer loop.

The inner nested for loop

The remainder of the doSomething function, including the entire nested for loop, is shown in Listing 2.

      for(int cCnt = 0;cCnt < colLim;cCnt = cCnt + 1){
        cout << rCnt * cCnt << " ";
      }//end inner loop

      cout << endl;
    }//end outer loop

  }//end doSomething function

Listing 2

A column counter named cCnt

The inner for loop, shown by the first three lines in Listing 2, initializes a column counter named cCnt and then updates it once at the end of each iteration of the inner loop.

The column counter is declared and initialized to a value of 0 by the initialization clause of the inner for loop.

The update clause increments the column counter by a value of 1 at the end of each iteration of the inner loop.

According to the conditional clause, the inner loop will continue to iterate for as long as the column counter is less than the value of colLim, which was set to 5 in Listing 1.

A five-to-one ratio

Thus, the inner for loop shown in Listing 2 will iterate five times during each iteration of the outer for loop that began in Listing 1.

(This is similar to the odometer in your car where each digit increments through ten units before the digit to its immediate left will increment by one unit.  In this program, however, the ratio is five-to-one instead of the ten-to-one ratio in your car.)

Display one value during each iteration

During each iteration of the inner loop, the first boldface statement in Listing 2 computes and display a single numeric value followed by a space as shown in Figure 1.

(If you were to run the program on an incredibly slow computer, you would see the values in Figure 1 being displayed one after the other moving from left to right across the screen during one set of iterations of the inner loop.)

This process continues until the conditional clause causes the inner loop to terminate, producing one row of output values as shown in Figure 1.

A new line is required

When the inner loop terminates, the second boldface statement in Listing 2 causes the screen cursor to drop down to the left end of the next line.  Then the row counter is incremented by a value of 1, and control returns to the top of the outer loop that began in Listing 1.

Perform another test

Back at the top of the outer loop, the value of the row counter is once more tested against the rowLim value of 3.  If the row counter is less than three, another iteration of the outer loop is executed, producing the next row of output values shown in Figure 1.

When the row counter value reaches 3, the outer loop terminates and the program terminates, having displayed three rows of five columns in each row as shown in Figure 1.

And that is probably more than you ever wanted to know about counting loops and nested loops.

The program named SentinelLoop01

This program uses an entry-condition while loop to illustrates a sentinel loop.

Compute the average grade

Let me explain the concept of a sentinel loop with an example.  Suppose I wanted to write a program that I can use to compute the average of a set of student grades.  Each time I run the program, the number of grades in the set is likely to be different.

I could write the program such that I could enter all of the grades in the set followed by a so-called sentinel value.  The program would continue adding and counting grades until I enter the sentinel value.  When I enter the sentinel value, the program will recognize that all of the grades have been entered, and will compute and display the average of all the grades in the set.

This is the scenario for which the program named SentinelLoop01 was designed.

The sentinel value

The sentinel value must be chosen carefully to ensure that it won't be confused with a valid data value.  In the case of grades, a good value for a sentinel value would be -1, since I never award negative grades to students.

Typical screen output

A typical output during the running of this program (for a set consisting of only three rather poor grades) might look like that shown in Figure 2.

Enter a grade or -1 to quit.
20.0
Enter a grade or -1 to quit.
40.0
Enter a grade or -1 to quit.
60.0
Enter a grade or -1 to quit.
-1
Average = 40
Figure 2

During each iteration of the sentinel loop, the program displays a prompt and then reads the value entered by the user.  When the user finally enters the sentinel value of -1, the program computes and displays the average grade and then terminates.

The doSomething function

Once again, I am going to discuss only the instance function named doSomething.  You can view the remainder of the program in Listing 10 near the end of the lesson.

The doSomething function begins in Listing 3.

  void doSomething(){
    int count = 0;
    double sum = 0;
    cout << "Enter a grade or -1 to quit." << endl;
    //Do a priming read
    double temp = 0;
    cin >> temp;

Listing 3

Initialize the counter and the accumulator

The function begins by setting the grade counter and the grade accumulator to zero.

Perform a priming read

Then the program instructs the user to either enter a grade, or to enter a -1 to quit.  Then it reads the input provided by the user.

(This initial input ahead of the loop that follows is commonly called a priming read.  This terminology is taken from the idea of priming a water pump to cause it to start pumping water.  If you don't know what it means to prime a pump, don't worry about it.  Understanding water pumps isn't critical to understanding this program.)

Interpreting the user input

If the user enters a -1 at this point, the body of the while loop that follows in Listing 4 will be bypassed, and the program will silently terminate without doing anything useful.

If the user enters the first grade at this point, it is saved in the double variable named temp that is declared and initialized in Listing 3.

The sentinel loop

The sentinel loop is shown in its entirety in Listing 4.

    while(temp != -1.0){
      count = count + 1;
      sum = sum + temp;
      cout << "Enter a grade or -1 to quit." << endl;
      cin >> temp;
    }//end while loop

Listing 4

As mentioned above, if the value of temp that was read in Listing 3 is -1.0, the body of the while loop will be bypassed and the program will terminate.

Process the first grade

On the other hand, if the user entered the first grade from the set in the priming read in Listing 3, the grade counter will be incremented and the grade will be added to the accumulator in the body of the while loop in Listing 4.

Read the next grade

Then the code in the body of the loop will request and read another grade, or a -1 to quit.

In this case, the -1 is the so-called sentinel value.  The loop will continue to iterate, reading grades, adding them to the accumulator, and incrementing the counter until the user enters the sentinel value of -1.

Behavior upon reading the sentinel value

When the user enters a value of -1, the conditional expression in the first line in Listing 4 will evaluate to false.  The body of the loop will be skipped and the loop will terminate causing control to pass from the loop structure in Listing 4 to the code in Listing 5.

Compute and display the average

The code in Listing 5 will first confirm that the counter value is not equal to 0.  This would be the case if the user had entered a -1 when requested to enter a grade in Listing 3.

    if(count != 0){
      cout << "Average = " << sum/count << endl;
    }//end if

  }//end doSomething function

Listing 5

If the counter value is ...

If the counter value is 0, the program will silently terminate.

If the counter value is not equal to 0, the code in Listing 5 will compute and display the average grade as the sum of the grades divided by the number of grades.

The type of the computed average

Because the sum of the grades is maintained as type double, the arithmetic will be performed as type double even though the number of grades is an integer. 

Thus, the output shown in Figure 2 would include a fractional part, such as 98.25 for example, if there is a fractional part.  (There was no fractional part in the average for the values illustrated in Figure 2.)

The program named SentinelLoop02

This program is another implementation of a sentinel loop using the do-while loop structure, (which is an exit-condition loop), instead of the while loop structure used in the previous program.

An exit-condition loop

The do-while loop differs from the while loop in that the test occurs at the end of the loop.  Having the test condition at the end guarantees that the body of the loop always executes at least one time.  This will eliminate the requirement for a priming read as was the case in Listing 3, causing this version of the sentinel-loop program to be somewhat simpler than the previous version.

Typical screen output

Typical screen output from this program is the same as for the previous program shown in Figure 2.

Syntax of a do-while loop

The general syntax of a do-while loop is shown in Figure 3.

do{
  //block of code
}while(test);
Figure 3

The doSomething function

As before, I will present and explain only the code in the function named doSomething.  You can view the remainder of the program in Listing 11 near the end of the lesson.

The doSomething function begins in Listing 6, which declares and initializes three working variables.

  void doSomething(){
    int count = -1;
    double sum = 0;
    double temp = 0;

Listing 6

Note that the grade counter variable named count is initialized to -1 instead of 0 as was the case in the previous program.

The do-while loop

The do-while loop is shown in its entirety in Listing 7.

    do{
      count = count + 1;
      sum = sum + temp;
      cout << "Enter a grade or -1 to quit." << endl;
      cin >> temp;
    }while(temp != -1.0);

Listing 7

No priming read is required

As mentioned earlier, having the test at the end of the loop guarantees that the body of the loop will be executed at least once regardless of the first value entered by the user.  This eliminates the need for a priming read as was used in the previous program.

Increment the grade counter and accumulate the grade

The body of  the loop begins by incrementing the grade counter by a value of 1.  During the first iteration, this will increment the counter from -1 to 0.

Then the code in the body of the loop adds the current value of temp into the sum.  During the first pass, because the value of temp was initialized to a value of zero, this will leave the value of the sum at zero.

Request and read an input value

Then the code in the body of the loop requests and reads an input value from the user.

If the user enters a value of ...

If the user enters a value of -1 during the first iteration, the loop will terminate immediately leaving the value of the counter and the value of the sum both at zero.

On the other hand, if the user enters a value other than -1, the loop will not terminate.  Rather control will go back to the top of the loop in Listing 7 where the grade counter will be incremented and the value of the grade will be added to the sum.

When the user enters the sentinel value

When the user finally enters the sentinel value of -1, the loop will terminate immediately leaving a correct value for the grade counter and a correct value for the sum of the grades in the variables named count and sum respectively.

Compute and display the average grade

At that point, control will transfer to the code in Listing 8.  If the value of the counter is greater than zero, the average grade will be computed and displayed.  Otherwise, the program will quietly terminate with no output.

    if(count > 0){
      cout << "Average = " << sum/count << endl;
    }//end if

  }//end doSomething function

Listing 8

Listing 8 signals the end of the doSomething function and the end of the program.

Complete Program Listings

Complete listings of the programs discussed in this lesson are shown below.
 
/*File:  NestedCounterLoop01.cpp
This C++ program illustrates the use of a pair of for loops
to create a nested counter loop.  Screen output from this 
program follows: 

0 0 0 0 0
0 1 2 3 4
0 2 4 6 8
**********************************************************/

#include <iostream>
using namespace std;

class NestedCounterLoop01{ 
  public:
  static void classMain(){
    NestedCounterLoop01* ptrToObject = 
                                 new NestedCounterLoop01();
    ptrToObject -> doSomething();
  }//End classMain function
  //-----------------------------------------------------//

  //An instance function of the NestedCounterLoop01 class
  void doSomething(){
    int rowLim = 3;
    int colLim = 5;

    for(int rCnt = 0;rCnt < rowLim;rCnt = rCnt + 1){
      for(int cCnt = 0;cCnt < colLim;cCnt = cCnt + 1){
        cout << rCnt * cCnt << " ";
      }//end inner loop
      cout << endl;
    }//end outer loop

  }//end doSomething function
};//End NestedCounterLoop01 class
//-------------------------------------------------------//

int main(){
  NestedCounterLoop01::classMain();
  return 0;
}//end main

Listing 9

 

/*File:  SentinelLoop01.cpp
This C++ program illustrates the use of a sentinel loop.
Typical screen output from this program follows: 

Enter a grade or -1 to quit.
20.0
Enter a grade or -1 to quit.
40.0
Enter a grade or -1 to quit.
60.0
Enter a grade or -1 to quit.
-1
Average = 40
**********************************************************/

#include <iostream>
using namespace std;

class SentinelLoop01{ 
  public:
  static void classMain(){
    SentinelLoop01* ptrToObject = new SentinelLoop01();
    ptrToObject -> doSomething();
  }//End classMain function
  //-----------------------------------------------------//

  //An instance function of the SentinelLoop01 class
  void doSomething(){
    int count = 0;
    double sum = 0;
    cout << "Enter a grade or -1 to quit." << endl;
    //Do a priming read
    double temp = 0;
    cin >> temp;

    while(temp != -1.0){
      count = count + 1;
      sum = sum + temp;
      cout << "Enter a grade or -1 to quit." << endl;
      cin >> temp;
    }//end while loop

    if(count != 0){
      cout << "Average = " << sum/count << endl;
    }//end if

  }//end doSomething function
};//End SentinelLoop01 class
//-------------------------------------------------------//

int main(){
  SentinelLoop01::classMain();
  return 0;
}//end main

Listing 10

 

/*File:  SentinelLoop02.cpp
This C++ program illustrates the use of a do-while loop to 
implement a sentinel loop.  Typical screen output from this
program follows: 

Enter a grade or -1 to quit.
20
Enter a grade or -1 to quit.
40
Enter a grade or -1 to quit.
60
Enter a grade or -1 to quit.
-1
Average = 40
**********************************************************/

#include <iostream>
using namespace std;

class SentinelLoop02{ 
  public:
  static void classMain(){
    SentinelLoop02* ptrToObject = new SentinelLoop02();
    ptrToObject -> doSomething();
  }//End classMain function
  //-----------------------------------------------------//

  //An instance function of the SentinelLoop02 class
  void doSomething(){
    int count = -1;
    double sum = 0;
    double temp = 0;

    do{
      count = count + 1;
      sum = sum + temp;
      cout << "Enter a grade or -1 to quit." << endl;
      cin >> temp;
    }while(temp != -1.0);

    if(count > 0){
      cout << "Average = " << sum/count << endl;
    }//end if

  }//end doSomething function
};//End SentinelLoop02 class
//-------------------------------------------------------//

int main(){
  SentinelLoop02::classMain();
  return 0;
}//end main

Listing 11


Copyright 2006, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which have gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

In addition to his programming expertise, Richard has many years of practical experience in Digital Signal Processing (DSP).  His first job after he earned his Bachelor's degree was doing DSP in the Seismic Research Department of Texas Instruments.  (TI is still a world leader in DSP.)  In the following years, he applied his programming and DSP expertise to other interesting areas including sonar and underwater acoustics.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com

-end-