Lab 4: Introduction to Selection and Repetition


Part 1: Selection — the if Statement

One of the easiest ways to make a program user-friendly is to make it menu driven. That is, rather than prompting the user in some vague sort of way, we present them with a menu of the choices available to them. This provides a friendly interface for the user, thus making the program easy to use.

For example, a simple 4-function calculator program might prompt the user by providing the following menu:

Please enter:
  + to add two numbers.
  - to subtract two numbers.
  * to multiple two numbers.
  / to divide two numbers.
The menu tells the user exactly what to enter.

This lab's exercise is to complete such a program, and at the same time learn about some of the C++ control structures for selection and repetition.

Getting Started

The file calculate.cpp contains some of the code to get started. You should download/copy this file into whatever directory/folder/project you are using for this lab exercise.

General if Statement

The general pattern for an if statement is:

if ( Condition )
  Statement1
[else
  Statement2 ]

Either Statement1 or Statement2, but not both, is executed based on the value of Condition. If Condition is true, then Statement1 is executed; otherwise (i.e., Condition is false), Statement2 is executed.

The square brackets [...] in the pattern indicates that the else clause is optional. Sometimes a simple if, without an else, is all that is needed.

Multi-branch if Statement

We can also chain several ifs together into one statement by making Statement2 an if statement. If we do this several times, we get a sequence of "nested ifs — one if inside another if inside another . . . In this case, we usually reformat it so that the various alternatives are clearly seen and to emphasize that in this multi-alternative structure, exactly one of the alternatives will be selected:

if (Condition1)
  Statement1
else if (Condition2)
  Statement2
...
else if (ConditionN)
  Statementn
else
  Statementn+1

Exactly one of the Statementi will be executed. It will be the first Statementi where Conditioni is true. All of the conditions that precede it must be false. If all conditions fail, then the fail-safe Statementn+1 is executed.

Helpful hint: Include a fail-safe else in a multibranch if for example, an output statement that displays an error message.

Also, there is no condition in this fail-safe statement.

Helpful hint: The last else in any if statement should not have a condition test.

Compound Statements

The patterns above for the if and multi-branch if statements used the singular noun "Statement", not "Statements." We may put only one statement in those places in the patterns.

But what if we need more than one statement? C++ allows us to wrap several statements in curly braces and the compiler will treat them as one statement:

{
  Statement1
  Statement2
  ...
  Statementn
}

Looking at and Finishing the Code

Take a few moments to study calculate.cpp, especially the code in main(). Make sure you understand the purpose of each statement before continuing on with this lab.

Ignoring its error-checking code, we can describe the behavior of main() follows:

Our program should display on the screen a greeting, followed by a menu of permissible operations. It should then prompt the user for one of these operations and then input that operation from the keyboard. Next, it should then prompt the user for the two operands for that operation and then input those operands from the keyboard. It should then compute the result of applying the user-specified operation to the two operands. It should conclude by displaying that result on the screen with appropriate labeling.

All of this behavior has been coded in main() except for the sentence written in boldface. Since there is no predefined C++ capability to directly perform that operation, we must write the code for this ourselves.

Behavior.

Our code must apply the operation to op1 and op2 and assign the resulting value to the variable result. More specifically, if the operation is '+', it should calculate the sum of the two operands and assign this sum to result. Otherwise, if the operation is '-', it should calculate their difference and assign that to result. Otherwise, if the operation is '*', our code should calculate the product of the two operands and assign it to result. Otherwise, if the operation is '/', it should calculate their quotient and assign this to result. And if it's none of these, it should just display an appropriate error message and stop execution.

Algorithm.

We can rewrite this description of behavior as follows so that it reads more like an algorithm:
If operation is '+':
    Assign the sum of the operands to result.
Otherwise, if operation is '-':
    Assign the difference of the operands to result.
Otherwise, if operation is '*':
    Assign the producte of the operands to result.
Otherwise, if operation is '/':
     Check if the denominator is zero.
     If it is, display an appropriate error message and assign 0 to result;
     otherwise, assign the quotient of the operands to result.

From this algorithm it is clear that a multi-branch if is needed and that a "nested" if will be needed in the last alternative,

Coding.

Testing and Debugging.

Coming Later ... an Aternative Selection Structure

The if statement used in our calculator has one drawback: it takes longer to reach the last alternative than it does earlier ones.

In general, selecting Statementi using a multi-branch if statement requires the evaluation of i conditions. This can be good because we can assume that the failed tests did, in fact, fail. When testing for ranges of values, this is very useful (and even necessary).

On the other hand, the evaluation of each condition consumes time, statements that occur later in the multi-branch if statement take longer to execute than do statements that occur earlier. An alternative that avoids this in certain cases is the switch statement that we will consider later.
 

Part 2: Repetition — the for and while Statements

We have seen that C++'s if statement selectively execute statements. In addition to selection structures, most programming languages also provide statements that make it possible to execute statements repeatedly, a programming construct commonly referred to as a loop.

Let's consider our calculator example. In it's present form, if we have several operations we want to perform, we must re-execute the program each time. It would certainly be more convenient if we could do several calculations during a single execution.

C++ provides various kinds of loops and in this lab exercise we will consider two of them:

This part of the lab exercise will add more functionality to our calculator program and make it more user-friendly.

Adding a New Operation — Exponentiation

First, however, we will add another operation to the menu of operations that our calculator can perform: an exponentiation operation that we will denote by '^'. It will calculate xn where the operands x and n are entered by the user. To simplify our task, we will assume that n is a nonnegative integer.

Note: As we know, exponentiation is available in C++ via the function pow() in the cmath library, but for this lab, we will not use it so that we can practice writing loops.

When faced with a new problem that involves general objects, it is often helpful to solve it by hand first with some specific examples. To illustrate, 2^0, 2^1, 2^2, 2^3, 2^4 — i.e., 20, 21, 22, 23, 24 — by hand, we might write out expressions for these computations:

2^0 = 20 = 1
2^1 = 21 = 2 * 1
2^2 = 22 = 2 * 2
2^3 = 23 = 2 * 2 * 2
2^4 = 24 = 2 * 2 * 2 * 2

The first case defines our starting point. The other cases make explicit what we already know: for xn, n tells us how many times we need to use x as factor. How do you know we have enough factors? You count them! Now, if we could only count in our programs!

Behavior. We can describe what we want to happen as follows:

The user will enter a base value op1 and an exponent value op2. Our code should begin by initializing result to one, and then repeatedly multiply result by the base value, with the number of repetitions being the exponent value.

Although we could worry about negative exponents and real-number exponents, for simplicity, we won't. So we won't check for this but simply include it for information in the prompt for entering operands; and we will simply allow whatever "garbage" value gets computed as the result.

Exer. 4.6 Add an exponentiation operation to calculate.cpp as follows:
  1. Add another item to MENU about exponentiation.

  2. Modify the if statement that checks the validity of the operation entered by the user so that it includes exponentiation.

  3. Modify the prompt to enter the operands by adding a comment that the exponent should be a nonnegative integer for ^.

  4. Add another alternative to the if-eise if ... in the //*** CALCULATE THE RESULT OF op1 operation op2 section to implement the ^ operation. For now, just have it set result to 1.

  5. Compile and execute your code to check that these additions are working correctly.

Implementing the Exponentiation Operation Using C++'s for Loop.
We can rewrite the description of the behavior of the exponentiation operation as the following algorithm:

  1. Initialize result to 1.
  2. For each value of count from 1 to exponent:
        Multiply result by base.

The C++ for loop is designed to facilitate the counting behavior required by step 2. The pattern for such a counting for loop is:

for (type variable = start_value; variable <= stop_value; variable++)
   statement
where type is a C++ numeric type, variable is the loop control variable used to do the counting, start_value is the first value, and stop_value is the last value.

For example, if we wanted to print x 20 times, we might write the following algorithm:

  1. For each i from 1 to 20:
    1. Display x.
which could be coded as:
for (int i = 1; i <= 20; i++)
  cout << x;

Exer. 4.7 Now complete the implementation begun in Exer. 4.6 of the algorithm above for the exponentiation operator. You already have implemented Step 1 of the algorithm so you need only add the for loop to implement Step 2.

Be sure to compile and test your program. Test exponentiation with several values. Make sure you test 0 as an exponent. Try values larger than 2 and 3 as both bases and exponents.

Characterizing Loops

The pattern for a C++ for loop is actually more general:

   for (initializationExpr; condition; stepExpr)
      statement
where initializationExpr is any initialization expression, condition is any boolean expression, and stepExpr is an arbitrary expression.

If for some reason we wanted to count downwards and output the multiples of 12 from 1200 to 12, then we'd have this algorithm

  1. For each i from 100 down to 1:
    1. Display 12 * i.
and code:
   for (int i = 100; i >= 1; i--)
     cout << 12 * i << endl;

The condition controls the loop. As long as it is true when it is tested, then the loop statement is executed. C++ for loops are controlled by conditions, just as if statements are controlled by conditions. As we shall see, each of the other C++ loop statements are also controlled by conditions.

A loop is categorized by when it evaluates its condition:

  1. A pretest loop evaluates its condition before its statements.
  2. A posttest loop evaluates its condition after its statements.
  3. An unrestricted loop evaluates its condition whenever you like.

The for loop is a pretest loop, because it evaluates its condition before the loop's statement is executed.

The for loop is designed primarily for problems that involve counting through ranges of numbers, or problems in which the number of repetitions can be determined in advance. The other C++ loops are general-purpose loops, designed for problems where the number of repetitions is not known in advance. We will use one of these — the while loop — to modify the program so that the user can do more than one calculation with each execution.

One Execution, Multiple Calculations

The algorithm that our program is using is basically as follows:

  1. Display opening information.
  2. Display a menu of operations and read operation.
  3. Display a prompt for two real values.
  4. Input op1 and op2.
  5. Compute result by applying operation to op1 and op2.
  6. Display result.
Using this algorithm, we have to re-run our program for every calculation, which is inconvenient for the user. A more convenient calculator would wrap some of the statements in a loop, so that the user could perform multiple calculations without having to re-run the program.

To incorporate the loop, we modify our algorithm:

  1. Display opening information.
  2. Loop:
    1. Display a menu of operations and read operation.
    2. Display a prompt for two real values.
    3. Input op1 and op2.
    4. Compute result by applying operation to op1 and op2.
    5. Display result.
    End loop.
Implementing the loop with a for-loop would require asking users in advance how many expressions they want to evaluate, which is certainly not very practical. A better solution, and one commonly used in menu-driven programs is to view quitting as an operation and provide an additional menu choice (e.g.., 'q') by which the user can indicate that they want to quit. The condition (operation == 'q') evaluates to true if the user wishes to quit.

Since checking this condition requires prior selection from the menu, our test for termination must be made after the user has made a selection. So we might modify our algorithm by moving "Loop:" down one line:

  1. Display opening information.
  2. Display a menu of operations and read operation.
  3. Loop while operation is not 'q':
    1. Display a prompt for two real values.
    2. Input op1 and op2.
    3. Compute result by applying operation to op1 and op2.
    4. Display result.
    End loop.
But clearly, this isn't enough, because unless the user selects 'q' as the first operation, the loop will be an infinite loop unless it includes another operation to be selected. And where should this be done? After the previous operation has been processed and the result displayed. So, we repeat instruction 2 again at the bottom of the loop:
  1. Display opening information.
  2. Display a menu of operations and read operation.
  3. Loop while operation is not 'q':
    1. Display a prompt for two real values.
    2. Input op1 and op2.
    3. Compute result by applying operation to op1 and op2.
    4. Display result.
    5. Display a menu of operations and read operation.
    End loop.
This repetition structure is characteristic of a while loop, which has the form:

   while (condition)
      statement
where condition is a boolean expression.
Exer. 4.8. Modify your source program to incorporate this approach. Then compile and test your program to ensure that it works correctly. Be sure to test each operator, including illegal ones.

Exer. 4.9. Extend calculate.cpp into a six-function calculator, as follows:

  1. Add a factorial operation ! that, given an integer n, computes n! = 1 * 2 * ... * (n-1) * n.
  2. Redesign the exponentiation operator so that it handles negative exponents.
Again, be sure to test the operators to ensure that they work correctly.

Hand In:


Lab Home Page


Report errors to Larry Nyhoff (nyhl@cs.calvin.edu)