练习17.1
定义一个保存三个
int值的tuple,并将其成员分别初始化为10、20和30。
解:
auto t = tuple<int, int, int>{10, 20, 30};
练习17.2
定义一个
tuple,保存一个string、一个vector<string>和一个pair<string, int>。
解:
auto t = tuple<string, vector<string>, pair<string, int> >
练习17.3
重写12.3节中的
TextQuery程序,使用tuple代替QueryResult类。你认为哪种设计更好?为什么?
解:
程序略。
我认为tuple更方便。
练习17.4
编写并测试你自己版本的
findBook函数。
解:
#include <iostream>#include <tuple>#include <string>#include <vector>#include <algorithm>#include <utility>#include <numeric>#include "ex_17_4_SalesData.h"using namespace std;// matches有三个成员:1.一个书店的索引。2.指向书店中元素的迭代器。3.指向书店中元素的迭代器。typedef tuple<vector<Sales_data>::size_type,vector<Sales_data>::const_iterator,vector<Sales_data>::const_iterator>matches;// files保存每家书店的销售记录// findBook返回一个vector,每家销售了给定书籍的书店在其中都有一项vector<matches> findBook(const vector<vector<Sales_data>> &files,const string &book){vector<matches> ret; //初始化为空vector// 对每家书店,查找给定书籍匹配的记录范围for (auto it = files.cbegin; it != files.cend(); ++it){// 查找具有相同ISBN的Sales_data范围,found是一个迭代器pairauto 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; //如果未找到匹配记录,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) // 对每家销售了给定书籍的书店// get<n>返回store中tuple的指定的成员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。
解:
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。
解:
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对象所包含的位模式:
(a) bitset<64> bitvec(32);// 0000000000000000000000000000000000000000000000000000000000100000(b) bitset<32> bv(1010101);// 00000000000011110110100110110101(c) string bstr; cin >> bstr; bitset<8> bv(bstr);// 根据输入的str转换成bitset
练习17.10
使用序列1、2、3、5、8、13、21初始化一个
bitset,将这些位置置位。对另一个bitset进行默认初始化,并编写一小段程序将其恰当的位置位。
解:
#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道题,你需要对数据结构做出什么改变(如果需要的话)?
解:
#include <iostream>#include <bitset>#include <utility>#include <string>#include <iostream>//class Quiztemplate<std::size_t N>class Quiz{public://constructorsQuiz() = default;Quiz(std::string& s) :bitquiz(s){ }//generate gradetemplate<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&);//update bitsetvoid update(std::pair<std::size_t, bool>);private:std::bitset<N> bitquiz;};#endiftemplate<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(){//Ex17_11std::string s = "1010101";Quiz<10> quiz(s);std::cout << quiz << std::endl;//EX17_12quiz.update(std::make_pair(1, true));std::cout << quiz << std::endl;//Ex17_13std::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
编写几个正则表达式,分别触发不同错误。运行你的程序,观察编译器对每个错误的输出。
解:
#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(){// for ex17.14// error_bracktry{regex r("[[:alnum:]+\\.(cpp|cxx|cc)$", regex::icase);}catch(regex_error e){cout << e.what() << " code: " << e.code() << endl;}// for ex17.15regex 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;elsecout << "Input word " << s << " is not okay!" <<endl;cout << "Please input a word! Input 'q' to quit!" << endl;}cout << endl;// for ex17.16r.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;elsecout << "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对象用"[^c]ei"进行初始化,将会发生什么?用此模式测试你的程序,检查你的答案是否正确。
解:
参考17.14。
练习17.17
更新你的程序,令它查找输入序列中所有违反”ei”语法规则的单词。
解:
#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
编写你自己版本的验证电话号码的程序。
解:
#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;// read each record from the input filewhile (getline(cin, s)){valid_record = false;// for each matching phone numberfor (sregex_iterator it(s.begin(), s.end(), r), end_it; it != end_it; ++it){valid_record = true;// check whether the number's formatting is validif (valid(*it))cout << "valid phone number: " << it->str() << endl;elsecout << "invalid phone number: " << it->str() << endl;}if (!valid_record)cout << "invalid record!" << endl;}return 0;}bool valid(const smatch& m){// if there is an open parenthesis before the area codeif (m[1].matched)// the area code must be followed by a close parenthesis// and followed immediately by the rest of the number or a spacereturn m[3].matched && (m[4].matched == 0 || m[4].str() == " ");else// then there can't be a close after the area code// the delimiters between the other two components must matchreturn !m[3].matched && m[4].str() == m[6].str();}
练习17.21
使用本节定义的
valid函数重写8.3.2节中的电话号码程序。
解:
#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);// fake function that makes the program compilestring 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 there is an open parenthesis before the area codeif (m[1].matched)// the area code must be followed by a close parenthesis// and followed immediately by the rest of the number or a spacereturn m[3].matched && (m[4].matched == 0 || m[4].str() == " ");else// then there can't be a close after the area code// the delimiters between the other two components must matchreturn !m[3].matched && m[4].str() == m[6].str();}bool read_record(istream& is, vector<PersonInfo>& people){if (is){string line, word; // will hold a line and word from input, respectively// read the input a line at a time until cin hits end-of-file (or another error)while (getline(is, line)){PersonInfo info; // create an object to hold this record's dataistringstream record(line); // bind record to the line we just readrecord >> info.name; // read the namewhile (record >> word) // read the phone numbersinfo.phones.push_back(word); // and store thempeople.push_back(info); // append this record to people}return true;}elsereturn 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){// for each entry in peopleostringstream formatted, badNums; // objects created on each loopfor (const auto &nums : entry.phones){for (sregex_iterator it(nums.begin(), nums.end(), r), end_it; it != end_it; ++it){// for each number// check whether the number's formatting is validif (!valid(*it))// string in badNumsbadNums << " " << nums;else// "writes" to formatted's stringformatted << " " << format(nums);}}if (badNums.str().empty()) // there were no bad numbersos << entry.name << " " // print the name<< formatted.str() << endl; // and reformatted numberselse // otherwise, print the name and bad numberscerr << "input error: " << entry.name<< " invalid number(s) " << badNums.str() << endl;}}
练习17.22
重写你的电话号码程序,使之允许在号码的三个部分之间放置任意多个空白符。
解:
参考17.21。
练习17.23
编写查找邮政编码的正则表达式。一个美国邮政编码可以由五位或九位数字组成。前五位数字和后四位数字之间可以用一个短横线分隔。
解:
#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 each matching zipcode numberfor (sregex_iterator it(s.begin(), s.end(), r), end_it;it != end_it; ++it){//! check whether the number's formatting is validif (valid(*it))cout << "valid zipcode number: " << it->str() << endl;elsecout << "invalid zipcode number: " << s << endl;}}return 0;}bool valid(const smatch& m){if ((m[2].matched)&&(!m[3].matched))return false;elsereturn true;}
练习17.24
编写你自己版本的重拍电话号码格式的程序。
解:
#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
重写你的电话号码程序,使之只输出每个人的第一个电话号码。
解:
#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。
解:
#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。
解:
#include <iostream>#include <random>#include<string>// default versionunsigned random_gen();// with seed spicifiedunsigned random_gen(unsigned seed);// with seed and range spicifiedunsigned 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节中的单词转换程序,允许对一个给定单词有多种转换方式,每次随机选择一种进行实际转换。
解:
#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;// read wirds from dictionarywhile (i >> str1 >> str2) {dict.emplace_back(str1, str2);}i.close();// sort words in vectorsort(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)));// read words from textwhile (i >> str1) {// find word in dictionaryvector<ps>::const_iterator it = find_if(dict.cbegin(), dict.cend(),[&str1](const ps &_ps){ return _ps.first == str1; });// if word doesn't exist in dictionaryif (it == dict.cend()) {// write it itselfcout << str1 << ' ';}else {// get random meaning of worduniform_int_distribution<unsigned> u (0, find_if(dict.cbegin(), dict.cend(),[&str1](const ps &_ps){ return _ps.first > str1; }) - it - 1);// write random meaningcout << (it + u(e))->second << ' ';}}return 0;}
练习17.34
编写一个程序,展示如何使用表17.17和表17.18中的每个操作符。
解:
略
练习17.35
修改第670页中的程序,打印2的平方根,但这次打印十六进制数字的大写形式。
解:
#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";}//17.36//Modify the program from the previous exercise to print the various floating-point values so that they line up in a column.#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的字符数组大小的行。
解:
//17.37//Use the unformatted version of getline to read a file a line at a time.//Test your program by giving it a file that contains empty lines as well as lines that are//longer than the character array that you pass to getline.#include <iostream>#include <fstream>#include <iomanip>using namespace std;//int main () {// ifstream myfile("F:\\Git\\Cpp-Primer\\ch17\\17_37_38\\test.txt");// if (myfile) cout << 1 << endl;// char sink [250];//// while(myfile.getline(sink,250))// {// cout << sink << endl;// }// return 0;//}//17.38//Extend your program from the previous exercise to print each word you read onto its own line.//#include <iostream>//#include <fstream>//#include <iomanip>////using namespace std;////int main () {// ifstream myfile ("F:\\Git\\Cpp-Primer\\ch17\\17_37_38\\test.txt");// char sink [250];//// while(myfile.getline(sink,250,' '))// {// cout << sink << endl;// }// return 0;//}int main(){std::cout << "Standard Output!\n";std::cerr << "Standard Error!\n";std::clog << "Standard Log??\n";}
练习17.38
扩展上一题中你的程序,将读入的每个单词打印到它所在的行。
解:
参考17.37。
练习17.39
对本节给出的
seek程序,编写你自己的版本。
解:
略
