练习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是一个迭代器pair
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; //如果未找到匹配记录,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 Quiz
template<std::size_t N>
class Quiz
{
public:
//constructors
Quiz() = default;
Quiz(std::string& s) :bitquiz(s){ }
//generate grade
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&);
//update bitset
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()
{
//Ex17_11
std::string s = "1010101";
Quiz<10> quiz(s);
std::cout << quiz << std::endl;
//EX17_12
quiz.update(std::make_pair(1, true));
std::cout << quiz << std::endl;
//Ex17_13
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
编写几个正则表达式,分别触发不同错误。运行你的程序,观察编译器对每个错误的输出。
解:
#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_brack
try{
regex r("[[:alnum:]+\\.(cpp|cxx|cc)$", regex::icase);
}
catch(regex_error e)
{
cout << e.what() << " code: " << e.code() << endl;
}
// for ex17.15
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;
// for ex17.16
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
对象用"[^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 file
while (getline(cin, s))
{
valid_record = false;
// for each matching phone number
for (sregex_iterator it(s.begin(), s.end(), r), end_it; it != end_it; ++it)
{
valid_record = true;
// check whether the number's formatting is valid
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 there is an open parenthesis before the area code
if (m[1].matched)
// the area code must be followed by a close parenthesis
// and followed immediately by the rest of the number or a space
return 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 match
return !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 compile
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 there is an open parenthesis before the area code
if (m[1].matched)
// the area code must be followed by a close parenthesis
// and followed immediately by the rest of the number or a space
return 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 match
return !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 data
istringstream record(line); // bind record to the line we just read
record >> info.name; // read the name
while (record >> word) // read the phone numbers
info.phones.push_back(word); // and store them
people.push_back(info); // append this record to people
}
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)
{
// for each entry in people
ostringstream formatted, badNums; // objects created on each loop
for (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 valid
if (!valid(*it))
// string in badNums
badNums << " " << nums;
else
// "writes" to formatted's string
formatted << " " << format(nums);
}
}
if (badNums.str().empty()) // there were no bad numbers
os << entry.name << " " // print the name
<< formatted.str() << endl; // and reformatted numbers
else // otherwise, print the name and bad numbers
cerr << "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 number
for (sregex_iterator it(s.begin(), s.end(), r), end_it;
it != end_it; ++it)
{
//! check whether the number's formatting is valid
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
编写你自己版本的重拍电话号码格式的程序。
解:
#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 version
unsigned random_gen();
// with seed spicified
unsigned random_gen(unsigned seed);
// with seed and range spicified
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节中的单词转换程序,允许对一个给定单词有多种转换方式,每次随机选择一种进行实际转换。
解:
#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 dictionary
while (i >> str1 >> str2) {
dict.emplace_back(str1, str2);
}
i.close();
// sort words in vector
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)));
// read words from text
while (i >> str1) {
// find word in dictionary
vector<ps>::const_iterator it = find_if(dict.cbegin(), dict.cend(),
[&str1](const ps &_ps){ return _ps.first == str1; });
// if word doesn't exist in dictionary
if (it == dict.cend()) {
// write it itself
cout << str1 << ' ';
}
else {
// get random meaning of word
uniform_int_distribution<unsigned> u (0, find_if(dict.cbegin(), dict.cend(),
[&str1](const ps &_ps){ return _ps.first > str1; }) - it - 1);
// write random meaning
cout << (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
程序,编写你自己的版本。
解:
略