Week 2
Sentinel Loop Patterns

How do you handle problems where the loop reads data from the user until some special value, or sentinel, is found to signal the end of the input? This is called a sentinel loop and its logical structure is:

Read a value
If the value is equal to the sentinel then
    Exit the loop
Process the value

There is no easy test at the beginning of the loop; you don't know when the sentinel is encountered until you've read a value. There are three ways to rearrange the statements to handle this situation.

The sentinels of Numenor
Week 2
The Primed Loop Pattern
Drinking from an old-fashioned water pump.

The Primed Loop Pattern

The primed-loop pattern is named after the old-west water pump which required users to pour water down the well to establish suction, before pumping began.

Here's what that looks like in pseudocode:

Prompt user and read in a value
While the value is not the sentinel
    Process the data value
    Prompt user and read next value

This is the classic way to process sentinel data. The code used to read each data value is duplicated, before the loop and at the end of the loop.

Here's a primed-sentinel-loop that sums a sequence, using 0 as the sentinel:

cout << "Add integers. Enter 0 when done." << endl;

int total = 0, value = -1;
cout << "> ";
cin >> value;               // Read before the loop
while (value != 0)          // Check for the sentinel
{
    total += value;         // No sentinel? Process
    cout << "> ";           // Prompt and read next item
    cin >> value;
}
cout << "Total: " << total << endl;
Week 2
The Loop-and-a-half Pattern

The Loop-and-a-half Pattern

The loop-and-a-half is a kind of loop that is available in some language (like Ada's Exit When), but which must be simulated in C and C++, by using if and break. This is another way to write a sentinel loop.

  • Write an endless loop (or a while loop with a necessary condition).
  • Add in if statement inside the loop which checks the sentinel.
  • If you find the sentinel, use break, which has the effect of immediately terminating the nearest enclosing loop.

The loop-and-a-half pattern has the advantage that it follows the natural structure: the read-until-sentinel pattern:

While True
    Prompt user and read value
    If value is the sentinel then
        break out of the loop
    Process the value

Note that this is an endless loop, where the only way to exit is by executing the break statement. Here's the same problem as on the previous page, using the loop-an-a-half pattern. You may want to look back and compare them.

while (true)                    // Endless loop
{
    cout << "> ";               // Prompt and read item
    cin >> value;
    if (value == 0) { break; }  // Sentinel? Leave loop
    total += value;             // No sentinel? Process
}
Week 2
The Flag-controlled Pattern
Decorative image of checkered flag.

The Flag-controlled Pattern

A third way to implement the read-until-sentinel pattern is to use a flag-controlled loop, where you introduce an additional Boolean variable just before the loop starts and set it to false. Inside the loop you read a data value and check the sentinel, just as in the loop-and-a-half.

Instead of a break statement, set your flag variable to true when the sentinel is read. Otherwise, you process that data value as normal:

Set finished to false    // Boolean control flag
while not finished
    read the value
    if value is the sentinel then
        set finished to true
    else
        process the variable

As we've done with the other two methods, here is the same program implemented as a flag-controlled sentinel loop:

bool finished;       // control flag
while (! finished)
{
    cout << "> ";               // Prompt and read item
    cin >> value;
    if (value == 0) 
    {
        finished = true; 
    }
    else
   {
        total += value;
  }
}

Which of these thre versions should you use? Eric Roberts, a professor at Stanford for many years, suggests that empirical studies demonstrate that students are more likely to write correct programs if they use the loop-and-a-half version than if they are forced to use some other strategy. If you're interested, follow the link to read Roberts' paper.

Week 2
Validating Data

Validating Data

When you read a value from cin, it is possible that the input may fail because the user entered invalid data. For instance:

cout << "Enter a number: ";
int n;
cin >> n;
cout << n << endl;

Suppose that the user types in one when asked to enter a number. Here's what happens:

  1. The cin object enters a failed state and will stop accepting any more input.
  2. The variable n will be set to 0.
  3. You can check for success by calling the member function fail() or by simply using a regular if statement. Here's a fragment that shows how to use if:

    int n;
    if (cin >> n) { cout << n << endl };
    else { cout << "Invalid input" << endl;  }

    And, here's a fragment which explicitly calles the fail() member function:

    int n;
    cin >> n;
    if (cin.fail()) { cout << "Invalid input" << endl };
    else  { cout << n << endl;}
    

    Recovering

    Inside a sentinel loop, you'd like to recover if the user inadvertently entered bad data.

    1. Call cin.clear() to allow cin to start accepting data once again.
    2. Consume the bad data by creating a string variable and reading it.
    while (true)                    // Endless loop
     {
        cout << "> ";               // Prompt and read item
        if (cin >> value) { 
            if (value == 0)   {break; {  // Sentinel? Leave loop
            total += value;             // No sentinel? Process
    {
        else { 
            cin.clear();                // Clear the fail flag
            string bad_data;            // store the bad data
            in >> bad_data;             // read it and ignore it
    {
    {