Accelerated C++ Solution to Exercise 2-4

Exercise 2-4

The framing program (Exercise 2-0) writes the mostly blank lines that separate the borders from the greeting one character at a time. Change the program so that it writes all the spaces needed in a single output expression.

Solution

The key to solving this problem is to use the string constructor statement that we learnt from chapter 1 on string.

std::string z(n, c) ;

This statements defines z as a variable of types std::string that initially contains n copies of the character c. Here, c must be a char, not a string or a string literal.

For example, the statement std::string z(10, ‘ ‘); will create a string variable z that is occupied of 10 space characters (char). We define char values using single quotes. (double quotes are for defining string values).

Bearing this in mind, I now demonstrate the high level Solution Strategy.

Solution Strategy ACPP Solution 2-4
Solution Strategy

The program will need to do the followings:

  • Ask the user for his first name so we can build the constant string greeting.
  • Ask the user for the padding value so we can (in conjunction with the readily build greeting string) compute the total frame rows and columns. We will also be able to build the constant string padStringLeftRight and padStringTopBottom.
  • Create an asymmetric for loop 1 r[0,rows) – the invariant r tells us which row we are at.
  • At the beginning of each row, reset the invariant c to 0 – i.e. always start from column 0 (the first column of the row).
  • Create an asymmetric while loop 2 c[0,cols) within loop 1 – the invariant c tells us which column we are at, keeping the row constant.
  • We perform the following checks during each complete loop to determine what to output:
    1. Are we at the exact position to output the greeting string? If so, output the constant string greeting, and increment the invariant c by size of the string. Else…
    2. Are we on any border? if so, output a string literal of 1 asterisk character, and increment the invariant c by 1. Else…
    3. Are we on the greeting line? if so, output the constant string padStringLeftRight, and increment the invariant c by size of the string. Else…
    4. Output the constant string padStringTopBottom, and increment the invariant c by size of the string.

Notes:

  • We use a for loop 1 (instead of while loop) because the increment of the invariant r (for rows) s always by 1 – using a for loop is easier and clearer. Nevertheless if we want it is also okay to use a while loop – just a matter of preference really.
  • We use a while loop 2 (instead of a for loop) because the increment of the invariant c (for columns) changes depending on what we output. Sometimes the increment is 1 (asterisk), sometimes the size of the string (greeting, padStringLeftRight, padStringTopBottom).

Putting this all together, we now have our complete program.

#include <iostream>
#include <string>

// say what standard-library names we use
using std::cin;
using std::endl;
using std::cout;
using std::string;

int main()
{
    // ask for the person's name
    cout << "Please enter your first name: ";

    // read the name
    string name = "";
    cin >> name;

    // ask user to provide the padding between frame border and greeting message
    cout << "Please specify an integer padding value: ";
    int inPad = 0;
    cin >> inPad;

    // build the message that we intend to write
    const string greeting = "Hello, " + name + "!";

    // build the constant padding parameters
    const int pad = inPad; // padding

    // compute the number of rows to write
    const int rows = ( pad * 2 ) + 3;

    // compute the number of columns to write
    const string::size_type cols = greeting.size() + ( pad * 2 ) + 2;

    // build the padding string that lives on the same row as the greeting, between the greeting and the border.
    const string padStringLeftRight( pad, ' ' );

    // build the padding string that lives between the top (or bottom) border and and greeting.
    const string padStringTopBottom( ( cols - 2 ) , ' ' );

    // write a blank line to separate the output from the input
    cout << endl;

    // write 'rows' rows of output
    // invariant: we have written r rows so far
    for ( int r = 0; r != rows ; ++r )
    {
        string::size_type c = 0;

        // invariant: we have written c characters so far in the current row
        while ( c != cols)
        {
            // Are we at the exact position where the greeting should start?
            if ( ( r == pad + 1 ) && ( c == pad + 1 ) )
            {
                // output the greeting message
                cout << greeting;
                c += greeting.size();
            }
            else
            {
                // are we on the border?
                if (
                       ( r == 0 )         // top row
                    || ( r == rows - 1 )  // or bottom row
                    || ( c == 0 )         // or left-most column
                    || ( c == cols - 1 )  // or right-most column
                   )
                {
                    // output the border asterisks
                    cout <<  "*";
                    ++c;
                }
                else
                {
                    // are we on the greeting line?
                    if ( r == pad + 1)
                    {
                        // output the empty spaces between left (or right) border and the greeting
                        cout << padStringLeftRight;
                        c += padStringLeftRight.size();
                    }

                    else
                    {
                        // output the empty spaces between top (or bottom) border and the greeting
                        cout << padStringTopBottom;
                        c += padStringTopBottom.size();
                    }
                }
            }
        }
        cout << endl;
    }
    return 0;
}

Further explanations

Just little bit more explanation, just for peace of mind.

To compute the total rows required by the frame, we sum the:

  • top and bottom padding (pad * 2),
  • top border, bottom border, and the greeting lines (1 + 1 + 1 = 3).

Hence we have this line in the code:

    // compute the number of rows to write
    const int rows = ( pad * 2 ) + 3;

To compute the total columns required by the frame, we sum the:

  • The size of the greeting message ( greeting.size() )
  • The size of the left and right paddings combined ( pad * 2 )
  • The size of left and right borders ( 1 + 1 = 2 ),

Also, as per the book chapter 2.4 (page 22), it is good habit to use the type string::size_type (instead of integer) when computing the sum of string sizes.

Hence we have this line in the code:

    // compute the number of columns to write
    const string::size_type cols = greeting.size() + ( pad * 2 ) + 2;

Result

The program compiles okay. Here is our output.

Please enter your first name: Johnny
Please specify an integer padding value: 3

**********************
*                    *
*                    *
*                    *
*   Hello, Johnny!   *
*                    *
*                    *
*                    *
**********************

Reference

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

6 thoughts on “Accelerated C++ Solution to Exercise 2-4”

    1. Hi Jared,

      That bit of code essentially is saying: Are we on the appropriate row r and column c? If yes, output the greeing image (and increment the c accordingly).

                  // Are we at the exact position where the greeting should start?
                  if ( ( r == pad + 1 ) && ( c == pad + 1 ) )
                  {
                      // output the greeting message
                      cout << greeting;
                      c += greeting.size();
                  }
      

      Try taking a look at the codes and diagrams above – hopefully should help clearing things up a bit.

      Good luck,
      Johnny

  1. Hi there, thanks for posting these answers but I have a question about a possible alternative that gives the same result but I want to know if its stylistically wrong to do….

    If you have a simple frame with pad =1 that looks like:

    *
    Hello, mboza2! *
    *

    Couldn’t this simply be written using nested if statements within the loop that cycles through the rows. 1 for if its the first or last row, 1for if its a row with the greeting, 1 for if its a row without the greeting.
    if(r==0 || r==rows){
    code to write stars along the whole border (row 0 and 4)
    }else{
    if(r==(rows+1)/2)
    code to write greeting line (row2 )
    else
    code to write the border star and leave the rest blank (rows 1 and 3)
    }

    Is this acceptable or will you run into problems with this nested if-statement style when writing bigger programs?

    Thanks!

  2. Hey John, I know I am a 1 year later, hope you still watch over this, how about combining functionality from exercise 2-3(letting user choose ammount of spacing before and after greeting message) with this one? I did try that, using a different approach tho and when I combined with your code I got some strange output, even before combining with your code output was odd,

    When combined with your code output is something like(not exactly like this but should give you idea):

    *
    *

    *

    If you want, I can post my code.

  3. You are way better than me. But in this exercise you were completely off the mark with your code. You did what was asked of you, but you did it the stupid (forceful) way.

    Good analysis, poor execution.

    P.S. Thumbs up for the page, very nice work. Thanks!

Leave a reply