Chapter 4 – Organizing Programs and Data
Solutions to exercises in Chapter 4 of Accelerated C++, “Organizing programs and data.”
Exercise 4-1
We noted in §4.2.3/65 that it is essential that the argument types in a call to max
match exactly. Will the following code work? If there is a problem, how would you fix it?
int maxlen; Student_info s; max(s.name.size(), maxlen);
Hint
Consider the return type of s.name.size()
.
Solution
There is a problem with the code. The type returned from s.name.size()
is string::size_type
, which does not match the type of maxlen
. To solve the problem, change maxlen
‘s declaration to string::size_type
.
#include <algorithm> #include <string> #include <vector> #ifdef _MSC_VER #undef max #endif using std::max; using std::string; using std::vector; struct Student_info { string name; double midterm, final; vector<double> homework; }; int main() { string::size_type maxlen; Student_info s; max(s.name.size(), maxlen); }
Exercise 4-2
Write a program to calculate the squares of int
values up to 100. The program should write two columns: The first lists the value; the second contains the square of that value. Use setw
to manage the output so that the values line up in columns.
Hint
Determine the widest outputs for both columns.
Solution
The widest number that is being squared is 100, which is three digits. 100 squared is 10000, which is 5 digits, so the solution belows sets a width of 4 prior to sending the base to output and set a width of 6 prior to sending the result to output. Setting the widths one larger than the values contained will preserve at least one space to the left of each value.
#include <iomanip> #include <iostream> using std::cout; using std::endl; using std::setw; int main() { for (int i = 1; i < 101; ++i) { cout << setw(4) << i << setw(6) << (i * i) << endl; } }
When executed the output will look like the following (shortened for space):
1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64 9 81 10 100 ... 95 9025 96 9216 97 9409 98 9604 99 9801 100 10000
Exercise 4-3
What happens if we rewrite the previous program to allow values up to but not including 1000 but neglect to change the arguments to setw
? Rewrite the program to be more robust in the face of changes that allow i
to grow without adjusting the setw
arguments.
Hint
Determine the length of the longest base and longest result and store those maximum lengths before starting to send the results to output.
Solution
If we change the program to output squares of bases up to 999 but don’t change the values provided to setw, the columns will begin to run together when the number of digits in the squares begins to equal 6.
1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64 9 81 10 100 ... 995990025 996992016 997994009 998996004 999998001
There are several ways to solve this problem, and in later chapters you will learn about some facilities in the standard library that would help you discover the maximum lengths that you need to find. This solution implements a function, digits
, that will return the number of digits in an integer. The program uses this function to find the length of the maximum base and the length of the square of the maximum base. It saves these lengths and uses them as parameters to setw
.
#include <iomanip> #include <iostream> using std::cout; using std::endl; using std::setw; /* Return the number of digits in n */ int digits(int n) { int result = 0; while (n) { /* Add 1 to the result... */ ++result; /* ...and remove a digit before the next pass. */ n /= 10; } return result; } int main() { const int max_base = 999; /* Find the number of digits in the highest base. */ int max_base_width = digits(max_base); /* Find the number of digits in the highest square. */ int max_result_width = digits(max_base * max_base); for (int i = 1; i <= max_base; ++i) { /* Add 1 to the maximum widths to allow for one space for padding. */ cout << setw(max_base_width + 1) << i << setw(max_result_width + 1) << (i * i) << endl; } }
When executed the output will look like the following (shortened for space):
1 1 2 4 3 9 4 16 5 25 6 36 7 49 8 64 9 81 10 100 ... 995 990025 996 992016 997 994009 998 996004 999 998001
Exercise 4-4
Now change your squares program to use double
values instead of int
s. Use manipulators to manage the output so that the values line up in columns.
Hint
Allow for fractional values, and change how you determine the number of digits in the highest base and square.
Solution
First, we’ll make a change to the digits
function to allow for the number of decimal places we want to see on the final outputs. Next, we’ll use the setprecision
manipulator to control the number of significant digits in the output.
#include <iomanip> #include <iostream> using std::cout; using std::endl; using std::setw; using std::setprecision; /* Return the number of digits in n */ int digits(double n, int places) { /* Initialize result to the number of desired decimal places. */ int result = places; while (n >= 1) { /* Add 1 to the result... */ ++result; /* ...and remove a digit before the next pass. */ n /= 10; } return result; } int main() { const double max_base = 99.9; /* Find the number of digits in the highest base. */ int max_base_width = digits(max_base, 2); /* Find the size of the highest square, allowing for 2 decimal places in the result. */ int max_result_width = digits(max_base * max_base, 2); for (double i = 1; i <= max_base; i += .1) { /* Add 2 to the maximum widths to allow for the decimal point and one space for padding. */ cout << setw(max_base_width + 2) << setprecision(max_base_width) << i << setw(max_result_width + 2) << setprecision(max_result_width) << (i * i) << endl; } }
When executed the output will look like the following (shortened for space):
1 1 1.1 1.21 1.2 1.44 1.3 1.69 1.4 1.96 1.5 2.25 1.6 2.56 1.7 2.89 1.8 3.24 1.9 3.61 2 4 2.1 4.41 2.2 4.84 2.3 5.29 2.4 5.76 2.5 6.25 ... 98.5 9702.25 98.6 9721.96 98.7 9741.69 98.8 9761.44 98.9 9781.21 99 9801 99.1 9820.81 99.2 9840.64 99.3 9860.49 99.4 9880.36 99.5 9900.25 99.6 9920.16 99.7 9940.09 99.8 9960.04 99.9 9980.01
Exercise 4-5
Write a function that reads words from an input stream and stores them in a vector
. Use that function both to write programs that count the number of words in the input, and to count how many times each word occurred.
Hint
Review the implementation of read_hw
in §4.1.3. Then, learn how to use separate compilation with your particular compiler.
Solution
This exercise asks for two programs that perform two different tasks sharing a single function. That suggest that we should partition the programs as described in §4.4.
First, create a header that declares the shared function’s signature.
#ifndef GUARD_read_words_h #define GUARD_read_words_h #include <vector> #include <istream> #include <string> std::istream& read_words(std::istream& is, std::vector<std::string>& words); #endif GUARD_read_words_h
Next, implement the read_words
function in a source file.
#include "read_words.h" using std::istream; using std::string; using std::vector; istream& read_words(istream& in, vector<string>& words) { if (in) { words.clear(); string word; while (in >> word) { words.push_back(word); } in.clear(); } return in; }
Now the programs can use this implementation. The first program will create a vector
of strings and call read_words
, passing cin
and the vector
as parameters. The populated vector
will return the number of words it contains from its size()
function.
#include "read_words.h" #include <vector> #include <iostream> #include <string> using std::cin; using std::cout; using std::endl; using std::string; using std::vector; int main() { vector<string> words; cout << "Enter a few words, followed by end-of-file: " << endl;; if (read_words(cin, words)) { cout << "You entered " << words.size() << " words." << endl; } }
The second program borrows from the solution to exercise 3-3.
#include "read_words.h" #include <algorithm> #include <iostream> #include <string> #include <vector> using std::cin; using std::cout; using std::endl; using std::sort; using std::string; using std::vector; int main() { vector<string> words; cout << "Enter a few words, followed by end-of-file: " << endl;; if (read_words(cin, words)) { typedef vector<string>::size_type vec_sz; vec_sz size = words.size(); // Check that the user entered some words if (size == 0) { cout << endl << "You didn't enter any words. " "Please try again." << endl; return 1; } // sort the words sort(words.begin(), words.end()); string current_word; int count; // Set the initial word to the first word in the vector current_word = words[0]; // Set the initial count for the first word count = 1; // Invariant: we have counted current_index of the total words // in the vector for (vec_sz current_index = 1; current_index < size; ++current_index) { // Report the count for the current word if it does not match // the word at the current index in the vector, and reset the // count to zero so that it will one when the variable is // incremented outside the if statement. if (current_word != words[current_index]) { cout << "The word \"" << current_word << "\" appears " << count << " times." << endl; current_word = words[current_index]; count = 0; } ++count; } // Report the count for the final word cout << "The word \"" << current_word << "\" appears " << count << " times." << endl; } else { cout << "An error occurred during input." << endl; return 2; } // We have reported the count of all the words in the vector, so exit. return 0; }
Exercise 4-6
Rewrite the Student_info
structure, and the read
and grade
functions, so that they calculate each student’s grades as part of reading the input, and store only the final grade.
No Solution Yet…
Check back for the solution later.
Exercise 4-7
Write a program to calculate the average of the numbers stored in a vector<double>
.
No Solution Yet…
Check back for the solution later.
Exercise 4-8
If the following code is legal, what can we infer about the return type of f
?
double d = f()[n];
No Solution Yet…
Check back for the solution later.