Chapter 7

Abstraction and Information Hiding


Page 305

7.1 To make the function todigit more robust it should trap values that aren't digits and return equivalent values:

    if (isdigit(c))
    {
        return c - '0';
    }
    else
    {
        return c;          // return value unchanged, not a digit
    }

This way the function returns a value even when its precondition isn't satisfied.

7.2 "Zebra" < "aardvark" because, in the ASCII system, the character 'Z' preceeds the character 'a'. In fact all upper caseletters come before any lowercase letter. When the case is the same , as is the case with "aardvark" and "yak" the ASCII order is the same as lexicographical (alphabetical) order.

7.3 The statement

    cout << 'a' + 3 << endl;
prints 100 because addition promotes its operands to the 'higher' type, in this case to int. As an int, 'a' is 97 in ASCII, so 100 is printed. When 100 is cast to a char by the statement
    cout << char ('a' + 3) << endl;
then 'd' is printed because 100 is ASCII for 'd'.

7.4 iscntrl('\t') is TRUE (non-zero) because tab, the char '\t' is a control character (it's CNTRL-I). isspace('\t') is TRUE (non-zero) as well since the tab character is whitespace. Finally, islower('\t') is FALSE (zero) since the tab character is not in the range 'a'..'z'.

7.5

bool IsVowel(char ch)
// postcondition: returns 1 if ch is a vowel, i.e., 'a', 'e', 'i', 'o',' u'
//                         returns 0 otherwise
{
    char check = tolower(ch);             // make lowercase
    if (check == 'a' || check == 'e' || check == 'i' ||
         check == 'o' || check == 'u')
    {
        return true;
    }
    else
    {
        return false;
    }
}
Alternatively body can be:
    return (check == 'a' || check == 'e' || check == 'i' 
                 || check == 'o' || check == 'u');

To write IsConsonant, just use as the function body:

    return ! IsVowel(ch);
since any non-vowel is a consonant.

7.6 The function below returns true instead of 1, and false instead of 0 (it is boolean-valued rather than int-valued).

bool IsPalindrome(String s)
// postcondition: returns true if s is a palindrome, else false
{
    int mid = s.Length()/2;       // middle char
    int last = s.Length() - 1;    // last char

    for(k=0; k < mid; k += 1)
    {
        if (s[k] != s[last])
        {
            return false;
        }
        last -= 1;                        // move towards 'left'
    }
    return true;                          // all chars match, is palindrome
}

It's possible to dispense with local variable 'last' and use as the if statement:

    if (s[k] != s[len-k-1])

where int len = s.Length();

7.7

void MakeLower(string & s)
// postcondition: all letters in s are lower case
{
    int len = s.Length();
    int k;
    for(k=0; k < len; k++)
    {
        s[k] = tolower(s[k]);            // converts to lower as necessary
    }
}

s is a reference parameter because its characters are changing.

7.8

int atoi(string s)
// precondition: s represents a valid integer, e.g., "1234", NOT "a123"
//               this function only deals with non-negative numbers
{
    int value = 0;
    int k;
    int len = s.Length();
    
    for(k=0; k < len; k++)
    {
        value *= 10;                  // shift to the left (multiply by 10)
        value += todigit(s[k]);       // see problem 1 above
    }
    return value;
}

Page 314

7.9 There are a few modifications needed. If the user is going to enter a group's name, then getline will need to be used because many/most group names contain whitespace. When getline is used with cin (and with any stream) then ALL input operations should be made using getline. Otherwise an extraction >> operation may occur that fails to read the '\n' character from the input stream. The getline will stop at this character storing nothing in the corresponding string.

For example:

    string first,last;

    cin >> first;
    getline(cin,last);

    cout << first << " is first " << last << " is last" << endl;

if the user enters "super" and presses return, the input for 'last' will read the return key pressed after "super" and the output will be:

    super is first is last

since to process getline(cin,last) all chars up to the unprocessed '\n' are read (and there are no such characters). The getline then proceeds after reading the '\n' but NOT storing it in 'last'.

Then an if statement is need with:

    getline(cin,title);
    if(group == s)
    {
        cout << title << endl;
    }

The variable title is necessary since s is used to compare groups AFTER the second line of input is read.

7.10 Since the statement input.get(ch); sets 'ch' to have a value, it must be passed by reference (a value parameter cannot be changed when it is passed to a function with the change communicated back).

7.11 To prompt for the name of the output file the following can be used:

    cout << "enter name of output file: ";
    cin >> filename;

    ofstream output(filename);

and the body of the while loop can be the single statement:

    output << ch;

or, alternatively,

    output.put(ch);

Where the put member function is the 'opposite' of the get function.

7.12 To modify upper to lower case letters, use the statement:

    output << tolower(ch);

7.13 To rewrite the while loop as a for-loop see below:

   for(getline(input,s); input.good(); getline(input,s))
   {
      numLines += 1;
      numChars += s.Length();
   }