I l@ve RuBoard Previous Section Next Section

1.4 Writing Conditional and Loop Statements

By default, statements are executed once in sequence, beginning with the first statement of main(). In the preceding section, we had a peek at the if statement. The if statement allows us to execute conditionally one or a sequence of statements based on the truth evaluation of an expression. An optional else clause allows us to test multiple truth conditions. A looping statement allows us to repeat one or a sequence of statements based on the truth evaluation of an expression. The following pseudo-code program makes use of two looping statements (#1 and #2), one if statement (#5), one if-else statement (#3), and a second conditional statement called a switch statement (#4).



// Pseudo code: General logic of our program 


while the user wants to guess a sequence 


{ #1 


    display the sequence 


    while the guess is not correct and 


          the user wants to guess again 


    { #2 


         read guess 


         increment number-of-tries count 


         if the guess is correct 


         { #3 


              increment correct-guess count 


              set got_it to true 


         } else { 


              express regret that the user has guessed wrong 


                   generate a different response based on the 


                   current number of guesses by the user // #4 


              ask the user if she wants to guess again 


              read response 


              if user says no // #5 


              set go_for_it to false 


         } 


    } 


} 

Conditional Statements

The condition expression of the if statement must be within parentheses. If it is true, the statement immediately following the if statement is executed:



// #5 


if ( usr_rsp == 'N' || usr_rsp == 'n' ) 


     go_for_it = false; 

If multiple statements must be executed, they must be enclosed in curly braces following the if statement (this is called a statement block):



//#3 


if ( usr_guess == next_elem ) 


{ // begins statement block 


     num_right++; 


     got_it = true; 


} // ends statement block 

A common beginner mistake is to forget the statement block:



// oops: the statement block is missing 


// only num_cor++ is part of if statement 


// got_it = true; is executed unconditionally 





if ( usr_guess == next_elem ) 


     num_cor++; 


     got_it = true; 

The indentation of got_it reflects the programmer's intention. Unfortunately, it does not reflect the program's behavior. The increment of num_cor is associated with the if statement and is executed only when the user's guess is equal to the value of next_elem. got_it, however, is not associated with the if statement because we forgot to surround the two statements within a statement block. got_it is always set to true in this example regardless of what the user guesses.

The if statement also supports an else clause. An else clause represents one or a block of statements to be executed if the tested condition is false. For example,



if ( usr_guess == next_elem ) 


{ 


     // user guessed correctly 


} 


else 


{ 


     // user guessed incorrectly 


} 

A second use of the else clause is to string together two or more if statements. For example, if the user guesses incorrectly, we want our response to differ based on the number of guesses. We could write the three tests as independent if statements:



if ( num_tries == 1 ) 


     cout << "Oops! Nice guess but not quite it.\n"; 





if ( num_tries == 2 ) 


     cout << "Hmm. Sorry. Wrong a second time.\n"; 





if ( num_tries == 3 ) 


     cout << "Ah, this is harder than it looks, isn't it?\n"; 

However, only one of the three conditions can be true at any one time. If one of the if statements is true, the others must be false. We can reflect the relationship among the if statements by stringing them together with a series of else-if clauses:



if ( num_tries == 1 ) 


     cout << "Oops! Nice guess but not quite it.\n"; 


else 


if ( num_tries == 2 ) 


     cout << "Hmm. Sorry. Wrong again.\n"; 


else 


if ( num_tries == 3 ) 


     cout << "Ah, this is harder than it looks, isn't it?\n"; 


else 


     cout << "It must be getting pretty frustrating by now!\n"; 

The first if statement's condition is evaluated. If it is true, the statement following it is executed and the subsequent else-if clauses are not evaluated. If the first if statement's condition evaluates to false, the next one is evaluated, and so on, until one of the conditions evaluates to true, or, if num_tries is greater than 3, all the conditions are false and the final else clause is executed.

One confusing aspect of nested if-else clauses is the difficulty of organizing their logic correctly. For example, we'd like to use our if-else statement to divide the program logic into two cases: when the user guesses correctly and when the user guesses incorrectly. This first attempt doesn't work quite as we intended:



if ( usr_guess == next_elem ) 


{ 


     // user guessed correctly 


} 


else 


if ( num_tries == 1 ) 


     // ... output response 


else 


if ( num_tries == 2 ) 


     // ... output response 


else 


if ( num_tries == 3 ) 


     // ... output response 


else 


     // ... output response 





// now ask user if she wants to guess again 


// but only if she has guessed wrong 


// oops! where can we place it? 

Each else-if clause has unintentionally been made an alternative to guessing the value correctly. As a result, we have no place to put the second part of our code to handle the user having guessed incorrectly. Here is the correct organization:



if ( usr_guess == next_elem ) 


{ 


     // user guessed correctly 


} 


else 


{  // user guessed incorrectly 


   if ( num_tries == 1 ) 


        // ... 


   else 


   if ( num_tries == 2 ) 


        // ... 


   else 


   if ( num_tries == 3 ) 


        // ... 


   else // ... 





   cout << "Want to try again? (Y/N) "; 


   char usr_rsp; 


   cin >> usr_rsp; 





   if ( usr_rsp == 'N' || usr_rsp == 'n' ) 


        go_for_it = false; 


} 

If the value of the condition being tested is an integral type, we can replace the if-else-if set of clauses with a switch statement:



// equivalent to if-else-if clauses above 


switch ( num_tries ) 


{ 


  case 1: 


       cout << "Oops! Nice guess but not quite it.\n"; 


       break; 





  case 2: 


       cout << "Hmm. Sorry. Wrong again.\n"; 


       break; 





  case 3: 


       cout << "Ah, this is harder than it looks, isn't it?\n"; 


       break; 





  default: 


       cout << "It must be getting pretty frustrating by now!\n"; 


       break; 


} 

The switch keyword is followed by an expression enclosed in parentheses (yes, the name of an object serves as an expression). The expression must evaluate to an integral value. A series of case labels follows the switch keyword, each specifying a constant expression. The result of the expression is compared against each case label in turn. If there is a match, the statements following the case label are executed. If there is no match and the default label is present, the statements following the default label are executed. If there is no match and no default label, nothing happens.

Why did I place a break statement at the end of each case label? Each case label is tested in turn against the expression's value. Each nonmatching case label is skipped in turn. When a case label matches the value, execution begins with the statement following the case label. The "gotcha" is that execution continues through each subsequent case statement until the end of the switch statement. If num_tries equals 2, for example, and if there was no break statement, the output would look like this:



// output if num_tries == 2 and 


//    we had forgotten the break statements 


Hmm. Sorry. Wrong again. 


Ah, this is harder than it looks, isn't it? 


It must be getting pretty frustrating by now! 

IAfter a case label is matched, all the case labels following the matched case label are also executed unless we explicitly break off execution. This is what the break statement does. Why, you're probably asking, is the switch statement designed this way? Here is an example of this fall-through behavior being just right:



switch ( next_char ) 


{ 


  case 'a': case 'A': 


  case 'e': case 'E': 


  case 'i': case 'I': 


  case 'o': case 'O': 


  case 'u': case 'U': 


       ++vowel_cnt; 


       break; 


  // ... 


} 

Loop Statements

A loop statement executes a statement or statement block as long as the condition expression evaluates as true. Our program requires two loop statements, one nested within the other:



while the user wants to guess a sequence 


{ 


    display the sequence 


    while the guess is not correct and 


          the user wants to guess again 


} 

The C++ while loop maps nicely to our needs:



bool next_seq = true;   // show next sequence? 


bool go_for_it = true;  // user wants to guess? 


bool got_it = false;    // user guessed correctly? 


int  num_tries = 0;     // number of user guesses 


int  num_right = 0;     // number of correct answers 





while ( next_seq == true ) 


{ 


        // display sequence to user 


        while (( got_it == false ) && 


               ( go_for_it == true )) 


        { 


             int usr_guess; 


             cin >> usr_guess; 


             num_tries++; 





              if ( usr_guess == next_elem ) 


              { 


                   got_it = true; 


                   num_cor++; 


              } 


              else 


              {  // user guessed incorrectly 


                  // tell user answer is wrong 


                  // ask user if she wants to try again 


                  if ( usr_rsp == 'N' || usr_rsp == 'n' ) 


                       go_for_it = false; 


              } 


        } // end of nested while loop 





        cout << "Want to try another sequence? (Y/N) " 


        char try_again; 


        cin >> try-again; 





        if ( try_again == 'N' || try_again == 'n' ) 


             next_seq = false; 


} // end of while( next_seq == true ) 

A while loop begins by evaluating the conditional expression within parentheses. If it is true, the statement or statement block following the while loop is executed. After the statement is executed, the expression is reevaluated. This evaluation/execution cycle continues until the expression evaluates to false. Typically, some condition within the executing statement block sets the expression to false. If the expression never evaluates to false, we say that we have mistakenly fallen into an infinite loop.

Our outer while loop executes until the user says she wishes to stop:



bool next_seq = true; 


while ( next_seq == true ) 


{ 


        // ... 


        if ( try_again == 'N' || try_again == 'n' ) 


             next_seq = false; 


} 

Were next_seq initialized to false, the statement block would not be executed. The nested while loop allowing our user multiple guesses behaves similarly.

A loop can be terminated within the body of its code sequence by the execution of a break statement. In the following code fragment, for example, the while loop executes until tries_cnt equals max_tries. If the user guesses the correct answer, however, the loop is terminated using the break statement:



int max_tries = 3; 


int tries_cnt = 0; 


while ( tries_cnt < max_tries ) 


{ 


   // read user guess 


   if ( usr_guess == next_elem ) 


        break;  // terminate loop 





   tries_cnt++; 


   // more stuff 


} 

The program can short-circuit execution of the current iteration of the loop by executing a continue statement. For example, consider the following program fragment in which all words of fewer than four characters are discarded:



string word; 


const int min_size = 4; 


while ( cin >> word ) 


{ 


   if ( word.size() < min_size ) 


        // terminates this iteration 


        continue; 


   // reach here only if the word is 


   // greater than or equal min-size ... 


   process_text( word ); 


} 

If word is less than min_size, the continue statement is executed. The continue statement causes the current loop iteration to terminate: The remainder of the while loop body ?in this case, process_text() ?is not evaluated. Rather, the loop begins again with a new evaluation of the condition expression, which reads another string into word. If word is greater than or equal to min_size, the entire while loop body is evaluated. In this way, all words of fewer than four characters are discarded.

    I l@ve RuBoard Previous Section Next Section