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?
1 2 3 |
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
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
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.
1 2 3 4 5 6 7 8 9 10 |
#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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
#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
?
1 |
double d = f()[n]; |
No Solution Yet…
Check back for the solution later.
Hi,
thanks for these solutions, i have a question about the exercise 4-5, the second program will only count how many times the first word occurred ? and then why did we sort the words ?
thanks !!
This function will read all the words in the vector. We just initialize the currentWord to the first entry in the vector to have an entry to compare to. So the first thing that happens in the for loop is to check if the currentWord (which is the first entry in the vector) is not equal to the next entry in the vector. The reason we use the sort function is that we are only comparing entries that are next to each other in the vector (we are iterating through the vector one entry(index) at a time and comparing the current entry to the previous entry.) If we didn’t arrange the words alphabetically, this algorithm would not work. Try it…remove the call to the sort function and see what happens.
Hope this helps
Hello,
The Chapter 4 code in 4.1 does not seem to work in My Visual C++ 2010 express compiler so i was wondering what i could do, i have fixed it myself but it is not as good as it could be. Any Ideas?
Thank You.
thank u for sharing the solutions so far as the 4.5. Expecting the continual partitions.
I actually discovered another solution to Chapter 4 Ex 5 that uses less memory than the one listed here. The difference is really irrelevant for a program this small, and this is as far I’ve gotten in the book so I have no clue if it will be relevant later. But here it is if anyone’s curious!
Just to say how redundant the difference is. Mine uses 40bytes regardless of the input (on my virtual machine running Slackware with netbeans). While the listed one uses 40bytes until you enter the input, in which case it spikes up depending on how much you input.
Yes, that’s right, there’s not even a kilobyte of difference :p
[code]
#include “read_words.h”
#include
#include
#include
#include
using std::cin;
using std::cout;
using std::endl;
using std::sort;
using std::string;
using std::vector;
int main()
{
vector words;
cout << "Enter a few words, followed by end-of-file: " << endl;;
if (read_words(cin, words))
{
typedef vector::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;
}
[/code]
I copied the wrong code, herp derp.
#include
#include
#include
#include
#include “read_words.h”
using std::cin; using std::cout; using std::string;
using std::vector; using std::endl; using std::sort;
int main()
{
vector words;
cout << "Input a few words, followed by end-of-file: ";
if (read_words(cin, words))
{
typedef vector::size_type vec_sz;
vec_sz size = words.size();
// Confirm there have been words input
if (size == 0)
{
cout << endl << "No words have been input. Please try again"
<< endl;
return 1;
}
sort(words.begin(), words.end());
vec_sz current_word = 0;
// Invariant: We have counted current_index of the words in the vector
for (vec_sz current_index = 1; current_index < size; ++current_index)
{
// When words [current_index] no longer is the same as
// words[current_word], output the amount of times
// words[current_word] was used by subtracting it from
// words[current_index]
if (words[current_index] != words[current_word])
{
cout << "The word\"" << words[current_word] << "\" appears "
<< current_index – current_word << " time(s)." << endl;
current_word = current_index;
}
}
// Report the final word
cout << "The word\"" << words[current_word] << "\" appears "
<< size – current_word << " time(s)." << endl;
}
else
{
cout << "An error occurred during output." << endl;
return 2;
}
return 0;
}
I appreciate this work.
It’s a function that returns a pointer to a double. The declaration could be something like “double * f()”
This is for question 4-8