Accelerated C++ Solution to Exercise 2-7

Exercise 2-7

Write a program that count down from 10 to -5

Solution

Though it is tempting to just create a for loop (or a while loop) with an invariant that start from 10, output the invariant, increment the invariant by -1, and exit the loop when the invariant becomes smaller than -5, I have decided to write the program in a way that is consistent with the asymmetric range method that the author has taught us. i.e. make use of the asymmetric invariant range denotation i[0,End) – meaning creating an invariant i that start with the value of zero (and always zero!), with an end number representing the number of loops that we wish the invariant to go through.

So how do we find out how many loops we need?

  • If we are to only output 10 we need 1 loop (to display 1 number). 10 – 10 + 1 = 1. i.e. we need the asymmetric range of [0,1)
  • If we need to output 10 and 9, we need 2 loops (to display 2 numbers). 10 – 9 +1 = 2. i.e. we need the asymmetric range of [0,2)
  • If we need to output 10, 9, 8, we need 3 loops (to display 3 number). 10 – 8 + 1 = 3. i.e. we need the asymmetric range of [0,3)

See the pattern?

If we need to display N numbers (counting down), we need N loops, as calculated as:

N = (( EndNumber - StartNumber ) * (-1 ) ) + 1

So if we are to display 10, 9, 8 …. -3, -4, -5, we have:

  • StartNumber = 10
  • EndNumber = -5
  • NumLoops = (( EndNumber – StartNumber ) * (-1 ) ) + 1 = ( (-5 -10) * (-1) ) + 1 = 15 + 1 = 16 loops.

Our asymmetric invariant range therefore takes this denotation: i [0, 16)

Understanding these relationships will allow us to be consistent in the way we program. This knowledge will enable us to be robust in solving a problem like this: write a program that count down from 999 to – 777. i.e. we can simply repeat the above methodology (or algorithm) without scratching our heads and get stuck!

One more question before we move on, what happen if we are counting up instead of counting down?

e.g. if we are to display -5, -4, -3, … 8, 9, 10, we have:

  • StartNumber = -5
  • EndNumber = 10
  • NumLoops = ( ( EndNumber – StartNumber ) * 1 ) + 1 = ( ( 10 – (-5) ) * 1 ) + 1 = 15 + 1 =16 loops.

If we need to display N numbers (counting up), we need N loops, as calculated as:

N = (( EndNumber - StartNumber ) * (+1 ) ) + 1

Our asymmetric invariant range still take the same denotation: i [0, 16)

The general equation for NumLoops is therefore:

NumLoops = ( (EndNumber - StartNumber) * directionFlag ) + 1
  • If counting up (i.e. EndNumber > StartNumber), the directionFlag is 1.
  • Otherwise, the directionFlag is -1.

Quite initiative really as we have a positive flag for counting up, and a negative flag for counting down.

Also note that if EndNumber == StartNumber, directionFlag becomes irrelevant because in that case the numLoops will be (0 * directionFlag) + 1, which is always 1.

The directionFlag also provides us a mean to determine whether we wish to increment the output value by +1 or -1.

The last bit is the actual counting and displaying the numbers. This is how I’d do it. (Note: there are many ways to do this!)

// Do the counting and display result row by row
for (int i = 0, iOut = startNumber; i != numLoops; ++i)
{
    std::cout << "Loop " << i << "| iOut = " << iOut << std::endl;
    iOut += directionFlag;
}

This is a for loop with i [0,numLoops) denotation. i.e. we are certain that there will be numLoops number of loops in total.

The first loop output the start value. At the end of the loop we increment the invariant by either +1 (counting up), or -1 (counting down). If the condition (i != numLoops) is true, loop again. Otherwise, the loop terminates.

The above snippet essentially isolate the invariant i component from the computed iOut component – for flexibility. In other words, the function of the invariant is to purely count the number of completed loops. We leave the computed component as free as it can be.

Putting everything together, we now have our full program:

#include <iostream>
using std::cin;
using std::cout;
using std::endl;

int main()
{
    // Display greeting message
    cout << "*****************************n"
         << "*** The Counting Program ****n"
         << "*****************************n";

    // Obtain startNumber and endNumber from the user
    cout << endl;
    cout << " Enter Start Number: ";
    int inputStartNumber;
    cin  >> inputStartNumber;
    cout << " Enter End Number: ";
    int inputEndNumber;
    cin  >> inputEndNumber;

    const int startNumber = inputStartNumber;
    const int endNumber = inputEndNumber;

    // Compute the directionFlag and numLoops
    int directionFlag, numLoops;
    if (endNumber > startNumber) {directionFlag = 1;}
    else {directionFlag = -1;}

    numLoops = ( (endNumber - startNumber) * directionFlag ) + 1;

    // Display the directionFlag and numLoops
    cout << " directionFlag = " << directionFlag
         << " | numLoops = " << numLoops << endl;

    // Do the counting and display result row by row
    for (int i = 0, iOut = startNumber; i != numLoops; ++i)
    {
        std::cout << "Loop " << i << "| iOut = " << iOut << std::endl;
        iOut += directionFlag;
    }
    return 0;
}

Result

The code compile okay. Let’s run it and see what we get!

Case 1: count from 10 to -5

*****************************
*** The Counting Program ****
*****************************

 Enter Start Number: 10
 Enter End Number: -5
 directionFlag = -1 | numLoops = 16
Loop 0| iOut = 10
Loop 1| iOut = 9
Loop 2| iOut = 8
Loop 3| iOut = 7
Loop 4| iOut = 6
Loop 5| iOut = 5
Loop 6| iOut = 4
Loop 7| iOut = 3
Loop 8| iOut = 2
Loop 9| iOut = 1
Loop 10| iOut = 0
Loop 11| iOut = -1
Loop 12| iOut = -2
Loop 13| iOut = -3
Loop 14| iOut = -4
Loop 15| iOut = -5

Case 2: count from -5 to 10

*****************************
*** The Counting Program ****
*****************************

 Enter Start Number: -5
 Enter End Number: 10
 directionFlag = 1 | numLoops = 16
Loop 0| iOut = -5
Loop 1| iOut = -4
Loop 2| iOut = -3
Loop 3| iOut = -2
Loop 4| iOut = -1
Loop 5| iOut = 0
Loop 6| iOut = 1
Loop 7| iOut = 2
Loop 8| iOut = 3
Loop 9| iOut = 4
Loop 10| iOut = 5
Loop 11| iOut = 6
Loop 12| iOut = 7
Loop 13| iOut = 8
Loop 14| iOut = 9
Loop 15| iOut = 10

Case 3: count from 10 to 10

*****************************
*** The Counting Program ****
*****************************

 Enter Start Number: 10
 Enter End Number: 10
 directionFlag = -1 | numLoops = 1
Loop 0| iOut = 10

 Reference

Koenig, Andrew & Moo, Barbara E., Accelerated C++, Addison-Wesley, 2000