练习17.1
定义一个保存三个int
值的 tuple
,并将其成员分别初始化为10、20和30。
解:
1 auto t = tuple<int , int , int >{10 , 20 , 30 };
练习17.2
定义一个 tuple
,保存一个 string
、一个vector<string>
和一个 pair<string, int>
。
解:
1 auto t = tuple<string, vector<string>, pair<string, int > >
练习17.3
重写12.3节中的 TextQuery
程序,使用 tuple
代替 QueryResult
类。你认为哪种设计更好?为什么?
解:
程序略。
我认为tuple
更方便。
练习17.4
编写并测试你自己版本的 findBook
函数。
解:
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 #include <iostream> #include <tuple> #include <string> #include <vector> #include <algorithm> #include <utility> #include <numeric> #include "ex_17_4_SalesData.h" using namespace std;typedef tuple<vector<Sales_data>::size_type, vector<Sales_data>::const_iterator, vector<Sales_data>::const_iterator> matches; vector<matches> findBook (const vector<vector<Sales_data>> &files, const string &book) { vector<matches> ret; for (auto it = files.cbegin; it != files.cend (); ++it) { auto found = equal_range (it->cbegin (), it->cend (), book, compareIsbn); if (found.first != found.second) ret.push_back (make_tuple (it - files.cbegin (), found.first, found.second)); } return ret; } void reportResults (istream &in, ostream &os, const vector<vector<Sales_data> > &files) { string s; while (in >> s){ auto trans = findBook (files, s); if (trans.empty ()){ cout << s << " not found in any stores" << endl; continue ; } for (const auto &store : trans) os << "store " << get <0 >(store) << " sales: " << accumulate (get <1 >(store), get <2 >(store), Sales_data (s)) << endl; } } int main () { return 0 ; }
练习17.5
重写 findBook
,令其返回一个 pair
,包含一个索引和一个迭代器pair。
解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 typedef std::pair<std::vector<Sales_data>::size_type, std::pair<std::vector<Sales_data>::const_iterator, std::vector<Sales_data>::const_iterator>> matches_pair; std::vector<matches_pair> findBook_pair (const std::vector<std::vector<Sales_data> > &files, const std::string &book) { std::vector<matches_pair> ret; for (auto it = files.cbegin (); it != files.cend (); ++it) { auto found = std::equal_range (it->cbegin (), it->cend (), book, compareIsbn); if (found.first != found.second) ret.push_back (std::make_pair (it - files.cbegin (), std::make_pair (found.first, found.second))); } return ret; }
练习17.6
重写 findBook
,不使用tuple
和pair
。
解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 struct matches_struct { std::vector<Sales_data>::size_type st; std::vector<Sales_data>::const_iterator first; std::vector<Sales_data>::const_iterator last; matches_struct (std::vector<Sales_data>::size_type s, std::vector<Sales_data>::const_iterator f, std::vector<Sales_data>::const_iterator l) : st (s), first (f), last (l) { } } ; std::vector<matches_struct> findBook_struct (const std::vector<std::vector<Sales_data> > &files, const std::string &book) { std::vector<matches_struct> ret; for (auto it = files.cbegin (); it != files.cend (); ++it) { auto found = std::equal_range (it->cbegin (), it->cend (), book, compareIsbn); if (found.first != found.second) ret.push_back (matches_struct (it - files.cbegin (), found.first, found.second)); } return ret; }
练习17.7
解释你更倾向于哪个版本的findBook
,为什么。
解:
使用tuple
的版本。很明显更加灵活方便。
练习17.8
在本节最后一段代码中,如果我们将Sales_data()
作为第三个参数传递给accumulate
,会发生什么?
解:
结果是0,以为Sales_data
是默认初始化的。
练习17.9
解释下列每个bitset
对象所包含的位模式:
1 2 3 4 5 6 (a) bitset<64> bitvec (32 ) ; (b) bitset<32> bv (1010101 ) ; (c) string bstr; cin >> bstr; bitset<8> bv (bstr) ;
练习17.10
使用序列1、2、3、5、8、13、21初始化一个bitset
,将这些位置置位。对另一个bitset
进行默认初始化,并编写一小段程序将其恰当的位置位。
解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> #include <bitset> #include <vector> int main () { std::vector<int > v = { 1 , 2 , 3 , 5 , 8 , 13 , 21 }; std::bitset<32> bset; for (auto i : v) bset.set (i); std::bitset<32> bset2; for (unsigned i = 0 ; i != 32 ; ++i) bset2[i] = bset[i]; std::cout <<bset <<std::endl; std::cout <<bset2<<std::endl; }
练习17.11
定义一个数据结构,包含一个整型对象,记录一个包含10个问题的真/假测验的解答。如果测验包含100道题,你需要对数据结构做出什么改变(如果需要的话)?
解:
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 #include <iostream> #include <bitset> #include <utility> #include <string> #include <iostream> template <std::size_t N>class Quiz { public : Quiz () = default ; Quiz (std::string& s) :bitquiz (s){ } template <std::size_t M> friend std::size_t grade (Quiz<M> const &, Quiz<M> const &) ; template <std::size_t M> friend std::ostream& operator <<(std::ostream&, Quiz<M> const &); void update (std::pair<std::size_t , bool >) ; private : std::bitset<N> bitquiz; }; #endif template <std::size_t N>void Quiz<N>::update (std::pair<std::size_t , bool > pair){ bitquiz.set (pair.first, pair.second); } template <std::size_t M>std::ostream& operator <<(std::ostream& os, Quiz<M> const & quiz) { os << quiz.bitquiz; return os; } template <std::size_t M>std::size_t grade (Quiz<M> const & corAns, Quiz<M> const & stuAns) { auto result = stuAns.bitquiz ^ corAns.bitquiz; result.flip (); return result.count (); } int main () { std::string s = "1010101" ; Quiz<10> quiz (s) ; std::cout << quiz << std::endl; quiz.update (std::make_pair (1 , true )); std::cout << quiz << std::endl; std::string answer = "10011" ; std::string stu_answer = "11001" ; Quiz<5> ans (answer) , stu_ans (stu_answer) ; std::cout << grade (ans, stu_ans) << std::endl; return 0 ; }
练习17.12
使用前一题中的数据结构,编写一个函数,它接受一个问题编号和一个表示真/假解答的值,函数根据这两个参数更新测验的解答。
解:
参考17.11。
练习17.13
编写一个整型对象,包含真/假测验的正确答案。使用它来为前两题中的数据结构生成测验成绩。
解:
参考17.11。
练习17.14
编写几个正则表达式,分别触发不同错误。运行你的程序,观察编译器对每个错误的输出。
解:
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 #include <iostream> using std::cout;using std::cin;using std::endl;#include <string> using std::string;#include <regex> using std::regex;using std::regex_error;int main () { try { regex r ("[[:alnum:]+\\.(cpp|cxx|cc)$" , regex::icase) ; } catch (regex_error e) { cout << e.what () << " code: " << e.code () << endl; } regex r ("[[:alpha:]]*[^c]ei[[:alpha:]]*" , regex::icase) ; string s; cout << "Please input a word! Input 'q' to quit!" << endl; while (cin >> s && s != "q" ) { if (std::regex_match (s, r)) cout << "Input word " << s << " is okay!" << endl; else cout << "Input word " << s << " is not okay!" <<endl; cout << "Please input a word! Input 'q' to quit!" << endl; } cout << endl; r.assign ("[^c]ei" , regex::icase); cout << "Please input a word! Input 'q' to quit!" << endl; while (cin >> s && s != "q" ) { if (std::regex_match (s, r)) cout << "Input word " << s << " is okay!" << endl; else cout << "Input word " << s << " is not okay!" <<endl; cout << "Please input a word! Input 'q' to quit!" << endl; } return 0 ; }
练习17.15
编写程序,使用模式查找违反“i在e之前,除非在c之后”规则的单词。你的程序应该提示用户输入一个单词,然后指出此单词是否符号要求。用一些违反和未违反规则的单词测试你的程序。
解:
参考17.14。
练习17.16
如果前一题程序中的regex
对象用"[^1]ei"
进行初始化,将会发生什么?用此模式测试你的程序,检查你的答案是否正确。
解:
参考17.14。
练习17.17
更新你的程序,令它查找输入序列中所有违反"ei"语法规则的单词。
解:
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 #include <iostream> using std::cout;using std::cin;using std::endl;#include <string> using std::string;#include <regex> using std::regex;using std::sregex_iterator;int main () { string s; cout << "Please input a sequence of words:" << endl; getline (cin, s); cout << endl; cout << "Word(s) that violiate the \"ei\" grammar rule:" << endl; string pattern ("[^c]ei" ) ; pattern = "[[:alpha:]]*" + pattern + "[[:alpha:]]*" ; regex r (pattern, regex::icase) ; for (sregex_iterator it (s.begin (), s.end (), r), end_it; it != end_it; ++it) cout << it->str () << endl; return 0 ; }
练习17.18
修改你的程序,忽略包含“ei`但并非拼写错误的单词,如“albeit”和“neighbor”。
解:
参考17.17。
练习17.19
为什么可以不先检查m[4]
是否匹配了就直接调用m[4].str()
?
解:
如果不匹配,则m[4].str()
返回空字符串。
练习17.20
编写你自己版本的验证电话号码的程序。
解:
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 #include <iostream> using std::cout;using std::cin;using std::endl;#include <string> using std::string;#include <regex> using std::regex;using std::sregex_iterator;using std::smatch;bool valid (const smatch& m) ;int main () { string phone = "(\\()?(\\d{ 3 })(\\))?([-. ])?(\\d{ 3 })([-. ]?)(\\d{ 4 })" ; regex r (phone) ; smatch m; string s; bool valid_record; while (getline (cin, s)) { valid_record = false ; for (sregex_iterator it (s.begin (), s.end (), r), end_it; it != end_it; ++it) { valid_record = true ; if (valid (*it)) cout << "valid phone number: " << it->str () << endl; else cout << "invalid phone number: " << it->str () << endl; } if (!valid_record) cout << "invalid record!" << endl; } return 0 ; } bool valid (const smatch& m) { if (m[1 ].matched) return m[3 ].matched && (m[4 ].matched == 0 || m[4 ].str () == " " ); else return !m[3 ].matched && m[4 ].str () == m[6 ].str (); }
练习17.21
使用本节定义的valid
函数重写8.3.2节中的电话号码程序。
解:
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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 #include <iostream> using std::cerr;using std::cout;using std::cin;using std::endl;using std::istream;using std::ostream;#include <fstream> using std::ifstream;using std::ofstream;#include <sstream> using std::istringstream;using std::ostringstream;#include <string> using std::string;#include <vector> using std::vector;#include <regex> using std::regex;using std::sregex_iterator;using std::smatch;struct PersonInfo { string name; vector<string> phones; }; bool valid (const smatch& m) ;bool read_record (istream& is, vector<PersonInfo>& people) ;void format_record (ostream& os, const vector<PersonInfo>& people) ;string format (const string &num) { return num; }int main () { vector<PersonInfo> people; string filename; cout << "Please input a record file name: " ; cin >> filename; cout << endl; ifstream fin (filename) ; if (read_record (fin, people)) { ofstream fout ("data\\result.txt" , ofstream::trunc) ; format_record(fout, people); } else { cout << "Fail to open file " << filename << endl; } return 0 ; } bool valid (const smatch& m) { if (m[1 ].matched) return m[3 ].matched && (m[4 ].matched == 0 || m[4 ].str () == " " ); else return !m[3 ].matched && m[4 ].str () == m[6 ].str (); } bool read_record (istream& is, vector<PersonInfo>& people) { if (is) { string line, word; while (getline (is, line)) { PersonInfo info; istringstream record (line) ; record >> info.name; while (record >> word) info.phones.push_back (word); people.push_back (info); } return true ; } else return false ; } void format_record (ostream& os, const vector<PersonInfo>& people) { string phone = "(\\()?(\\d{ 3 })(\\))?([-. ])?(\\d{ 3 })([-. ]?)(\\d{ 4 })" ; regex r (phone) ; smatch m; for (const auto &entry : people) { ostringstream formatted, badNums; for (const auto &nums : entry.phones) { for (sregex_iterator it (nums.begin (), nums.end (), r), end_it; it != end_it; ++it) { if (!valid (*it)) badNums << " " << nums; else formatted << " " << format(nums); } } if (badNums.str ().empty ()) os << entry.name << " " << formatted.str () << endl; else cerr << "input error: " << entry.name << " invalid number(s) " << badNums.str () << endl; } }
练习17.22
重写你的电话号码程序,使之允许在号码的三个部分之间放置任意多个空白符。
解:
参考17.21。
练习17.23
编写查找邮政编码的正则表达式。一个美国邮政编码可以由五位或九位数字组成。前五位数字和后四位数字之间可以用一个短横线分隔。
解:
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 #include <iostream> using std::cout;using std::cin;using std::endl;#include <string> using std::string;#include <regex> using std::regex;using std::sregex_iterator;using std::smatch;bool valid (const smatch& m) ;int main () { string zipcode = "(\\d{5})([-])?(\\d{4})?\\b" ; regex r (zipcode) ; smatch m; string s; while (getline (cin, s)) { for (sregex_iterator it (s.begin (), s.end (), r), end_it; it != end_it; ++it) { if (valid (*it)) cout << "valid zipcode number: " << it->str () << endl; else cout << "invalid zipcode number: " << s << endl; } } return 0 ; } bool valid (const smatch& m) { if ((m[2 ].matched)&&(!m[3 ].matched)) return false ; else return true ; }
练习17.24
编写你自己版本的重拍电话号码格式的程序。
解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> #include <regex> #include <string> using namespace std;string pattern = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ])?(\\d{4})" ; string format = "$2.$5.$7" ; regex r (pattern) ;string s; int main () { while (getline (cin,s)) { cout<<regex_replace (s,r,format)<<endl; } return 0 ; }
练习17.25
重写你的电话号码程序,使之只输出每个人的第一个电话号码。
解:
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 #include <iostream> #include <regex> #include <string> using namespace std;string pattern = "(\\()?(\\d{3})(\\))?([-. ])?(\\d{3})([-. ])?(\\d{4})" ; string fmt = "$2.$5.$7" ; regex r (pattern) ;string s; int main () { while (getline (cin,s)) { smatch result; regex_search (s,result,r); if (!result.empty ()) { cout<<result.prefix ()<<result.format(fmt)<<endl; } else { cout<<"Sorry, No match." <<endl; } } return 0 ; }
练习17.26
重写你的电话号码程序,使之对多于一个电话号码的人只输出第二个和后续号码。
解:
略
练习17.27
编写程序,将九位数字邮政编码的格式转换为 ddddd-dddd
。
解:
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 #include <iostream> #include <regex> #include <string> using namespace std;string pattern = "(\\d{5})([.- ])?(\\d{4})" ; string fmt = "$1-$3" ; regex r (pattern) ;string s; int main () { while (getline (cin,s)) { smatch result; regex_search (s,result, r); if (!result.empty ()) { cout<<result.format(fmt)<<endl; } else { cout<<"Sorry, No match." <<endl; } } return 0 ; }
练习17.28
编写函数,每次调用生成并返回一个均匀分布的随机unsigned int
。
解:
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 #include <iostream> #include <random> #include <string> unsigned random_gen () ;unsigned random_gen (unsigned seed) ;unsigned random_gen (unsigned seed, unsigned min, unsigned max) ;int main () { std::string temp; while (std::cin >> temp) std::cout << std::hex << random_gen (19 , 1 , 10 ) << std::endl; return 0 ; } unsigned random_gen () { static std::default_random_engine e; static std::uniform_int_distribution<unsigned > ud; return ud (e); } unsigned random_gen (unsigned seed) { static std::default_random_engine e (seed) ; static std::uniform_int_distribution<unsigned > ud; return ud (e); } unsigned random_gen (unsigned seed, unsigned min, unsigned max) { static std::default_random_engine e (seed) ; static std::uniform_int_distribution<unsigned > ud (min, max) ; return ud (e); }
练习17.29
修改上一题中编写的函数,允许用户提供一个种子作为可选参数。
解:
参考17.28。
练习17.30
再次修改你的程序,此次增加两个参数,表示函数允许返回的最小值和最大值。
解:
参考17.28。
练习17.31
对于本节中的游戏程序,如果在do
循环内定义b
和e
,会发生什么?
解:
由于引擎返回相同的随机数序列,因此眉不循环都会创建新的引擎,眉不循环都会生成相同的值。
练习17.32
如果我们在循环内定义resp
,会发生什么?
解:
会报错,while
条件中用到了resp
。
练习17.33
修改11.3.6节中的单词转换程序,允许对一个给定单词有多种转换方式,每次随机选择一种进行实际转换。
解:
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 #include <iostream> using std::cout;using std::endl;#include <fstream> using std::ifstream;#include <string> using std::string;#include <vector> using std::vector;#include <random> using std::default_random_engine;using std::uniform_int_distribution;#include <ctime> using std::time;#include <algorithm> using std::sort;using std::find_if;#include <utility> using std::pair;int main () { typedef pair<string, string> ps; ifstream i ("d.txt" ) ; vector<ps> dict; string str1, str2; while (i >> str1 >> str2) { dict.emplace_back (str1, str2); } i.close (); sort (dict.begin (), dict.end (), [](const ps &_ps1, const ps &_ps2){ return _ps1.first < _ps2.first; }); i.open ("i.txt" ); default_random_engine e (unsigned int (time(0 ))) ; while (i >> str1) { vector<ps>::const_iterator it = find_if (dict.cbegin (), dict.cend (), [&str1](const ps &_ps){ return _ps.first == str1; }); if (it == dict.cend ()) { cout << str1 << ' ' ; } else { uniform_int_distribution<unsigned > u (0 , find_if (dict.cbegin (), dict.cend (), [&str1](const ps &_ps){ return _ps.first > str1; }) - it - 1 ); cout << (it + u (e))->second << ' ' ; } } return 0 ; }
练习17.34
编写一个程序,展示如何使用表17.17和表17.18中的每个操作符。
解:
略
练习17.35
修改第670页中的程序,打印2的平方根,但这次打印十六进制数字的大写形式。
解:
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 #include <iostream> #include <iomanip> #include <math.h> using namespace std;int main () { cout <<"default format: " << 100 * sqrt (2.0 ) << '\n' << "scientific: " << scientific << 100 * sqrt (2.0 ) << '\n' << "fixed decimal: " << fixed << 100 * sqrt (2.0 ) << '\n' << "hexidecimal: " << uppercase << hexfloat << 100 * sqrt (2.0 ) << '\n' << "use defaults: " << defaultfloat << 100 * sqrt (2.0 ) << "\n\n" ; } #include <iostream> #include <iomanip> #include <math.h> using namespace std;int main () { cout <<left<<setw (15 ) << "default format:" <<setw (25 )<< right<< 100 * sqrt (2.0 ) << '\n' << left << setw (15 ) << "scientific:" << scientific << setw (25 ) << right << 100 * sqrt (2.0 ) << '\n' << left << setw (15 ) << "fixed decimal:" << setw (25 ) << fixed << right << 100 * sqrt (2.0 ) << '\n' << left << setw (15 ) << "hexidecimal:" << setw (25 ) << uppercase << hexfloat << right << 100 * sqrt (2.0 ) << '\n' << left << setw (15 ) << "use defaults:" << setw (25 ) << defaultfloat << right << 100 * sqrt (2.0 ) << "\n\n" ; }
练习17.36
修改上一题中的程序,打印不同的浮点数,使它们排成一列。
解:
参考17.36。
练习17.37
用未格式化版本的getline
逐行读取一个文件。测试你的程序,给定一个文件,既包含空行又包含长度超过你传递给geiline
的字符数组大小的行。
解:
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 #include <iostream> #include <fstream> #include <iomanip> using namespace std;int main () { std::cout << "Standard Output!\n" ; std::cerr << "Standard Error!\n" ; std::clog << "Standard Log??\n" ; }
练习17.38
扩展上一题中你的程序,将读入的每个单词打印到它所在的行。
解:
参考17.37。
练习17.39
对本节给出的 seek
程序,编写你自己的版本。
解:
略