10.01 头文件algorithm中定义了一个名为count的函数,它类似find,接受一对迭代器和一个值作为参数。count返回给定值在序列中出现的次数。编写程序,读取int序列存入vector中,打印有多少个元素的值等于给定值。
void test1001()
{
int ia[] = {1, 2, 2, 4, 3, 51, 2};
int num = 2;
vector<int> ivec(ia, ia+7);
int result = count(ivec.begin(), ivec.end(), num);
cout << result << endl;
}
10.02 重做上一题,但读取string序列存入list中。
void test1002()
{
string sa[] = {"1", "a", "abc", "abc", "abc", "bc", "abc"};
string str = "abc";
list<string> slst(sa, sa+7);
int result = count(slst.begin(), slst.end(), str);
cout << result << endl;
}
10.03 用accumulate求一个vector
void test1003()
{
int ia[] = {1, 2, 2, 4, 3, 51, 2};
vector<int> ivec(ia, ia+7);
int sum = accumulate(ivec.begin(), ivec.end(), 0);
cout << sum << endl;
}
10.04 假定v是一个vector
因为第三个参数是0,则编译器会认为其是一个int类型。最后的求和会损失精度。
10.05 在本节对名册(roster)调用equal的例子中,如果两个名册中保存的都是c风格字符串而不是string,会发生什么?
c风格字符串有begin(), end()函数吗?不能使用equal,可以使用strcmp。
10.06 编写程序,使用fill_n将一个序列中的int都设置为0。
void test1006()
{
vector<int> ivec(10);
for (int i = 0; i < 10; i++) {
ivec[i] = i+1;
}
fill_n(ivec.begin(), ivec.size(), 0);
}
10.07 下面程序是否有错误?如果有,请改正。
// a
vector<int> vec; list<int> lst; int i;
while (cin >> i)
lst.push_back(i);
copy(lst.cbegin(), lst.cend(), vec.begin());
// 有错误,vec的大小为0,直接将lst复制到vec,将会发生不可预料的错误。
在copy前加上:vec.resize(lst.size());
// b
vector<int> vec;
vec.reserve(10); // reserve是改变容器容量的,并不能改变容器大小。应改为resize。
fill_n(vec.begin(), 10, 0);
10.08 本节提到过,标准库算法不会改变他们所操作的容器的大小。为什么使用back_inserter不会使这一断言失效?
通过插入迭代器来插入元素,使用的是迭代器,而不是算法插入。
10.09 实现你自己的elimDups。测试你的程序,分别在读取输入后、调用unique后以及调用erase后打印vector的内容。
void elimDups(vector<string>& words)
{
sort(words.begin(), words.end()); // 按字典序重排words
for (auto i : words) {
cout << i << " "; // fox jumps over quick red red slow the the turtle
}
cout << endl;
auto end_unique = unique(words.begin(), words.end()); // 重排,使每个单词只出现一次,指向不重复区域之后的迭代器。
for (auto i : words) {
cout << i << " "; // fox jumps over quick red slow the turtle the red
}
cout << endl;
words.erase(end_unique, words.end());
for (auto i : words) {
cout << i << " "; // fox jumps over quick red slow the turtle
}
cout << endl;
}
10.10 你认为算法不改变容器大小的原因是什么?
标准库算法对迭代器而不是容器进行操作,因此,算法不能(直接)添加或删除元素。
10.11 编写程序,使用stable_sort和isShorter将传递给你的elimDups版本的vector排序。打印vector的内容,验证你的程序的正确性。
void test1009()
{
vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red", "turtle"};
elimDups(svec);
printVec(svec); // fox jumps over quick red slow the turtle
// use isShorter
sort(svec.begin(), svec.end(), isShorter);
printVec(svec); // fox red the over slow jumps quick turtle
// use stable_sort
stable_sort(svec.begin(), svec.end(), isShorter);
printVec(svec); // fox red the over slow jumps quick turtle
}
10.12 编写名为compareIsbn的函数,比较两个Sales_data对象的isbn()成员。使用这个函数排序一个保存Sales_data对象的vector。
bool compareIsbn(const Sales_data& sale1, const Sales_data& sale2)
{
return sale1.isbn().size() > sale2.isbn().size();
}
void test1012()
{
vector<Sales_data> sale_vec;
string bookNo = "";
for (int i = 0; i < 5; i++) {
bookNo += "s";
cout << bookNo << endl;
sale_vec.push_back(Sales_data(bookNo));
}
for (auto i : sale_vec) {
print(cout, i);
}
sort(sale_vec.begin(), sale_vec.end(), compareIsbn);
for (auto i : sale_vec) {
print(cout, i);
}
}
10.13 标准库定义了名为partition的算法,它接受一个谓词,对容器内容进行划分,使得谓词为true的值对排在容器的前半部分,而使谓词为false的值会排在后半部分。算法返回一个迭代器,指向最后一个使谓词为true的元素之后的位置。编写函数,接受一个string,返回一个bool值,指出string是否有5个或更多字符。使用此函数划分words。打印出长度大于等于5的元素。
bool isStrLongThan5(string& str)
{
return str.size() >= 5;
}
void test1013()
{
vector<string> svec = {"hello", "world", "bin", "is", "the", "banana"};
auto it = partition(svec.begin(), svec.end(), isStrLongThan5);
printVec(svec);
while (-- it != (svec.begin()-1)) {
cout << *it << " ";
}
}
10.14 编写一个lambda,接受两个int,返回它们的和。
auto sum = [](const int a, const int b) { return a+b; }
10.15 编写一个lambda,捕获它所在函数的int,并接受一个int参数。lambda应该返回捕获的int和int参数的和。
auto sum = [a](const int b) { return a+b; }
10.16 使用lambda编写你自己版本的biggies。
void biggies(vector<string>& words, size_t sz)
{
elimDups(words);
printVec(words);
stable_sort(words.begin(), words.end(), [](const string& str1, const string& str2){ return (str1.size()>=str2.size());});
find_if(words.begin(), words.end(), [sz](const string& str){ return str.size()>sz;});
printVec(words);
}
void test1016()
{
vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red", "turtle"};
biggies(svec, 4);
}
10.17 重新10.12的程序,在对sort的调用中使用lambda来代替函数compareIsbn。
sort(sale_vec.begin(), sale_vec.end(), [](const Sales_data& sale1, const Sales_data& sale2) { return sale1.isbn().size() > sale2.isbn().size(); });
// 将12题中的sort一行改为上述语句即可。
10.18 重写biggies,用partition代替find_if。我们在10.13中介绍了partition算法。
partition(words.begin(), words.end(), [sz](const string& str){ return str.size()>sz;});
// 将16题中的find_if一句改为上述数据即可。
10.19 用stable_partition重写前一题的程序,与stable_sort类似,在划分后的序列中维持原有元素的顺序。
stable_partition(words.begin(), words.end(), [sz](const string& str){ return str.size()>sz;});
// 将上题中的partition一行改为上述语句。
10.20 标准库定义了一个名为count_if的算法。类似find_if,此函数接受一对迭代器,表示一个输入范围,还接受一个谓词,会对输入范围中每个元素执行。count_if返回一个计数值,表示谓词有多少次为真。使用count_if重写我们程序中统计有多少单词长度超过6的部分。
void test1020()
{
int sz = 6;
vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red road", "turtle"};
int cnt = count_if(svec.begin(), svec.end(), [sz](const string& str){ return str.size()>sz;});
cout << cnt << endl;
}
10.21 编写一个lambda,捕获一个局部int变量,并递减变量值,直至它变为0.一旦变量变为0,再调用lambda应该不再递减变量.lambda应该返回一个bool值,指出捕获的变量是否为0.
void test1021()
{
int val = 5;
auto f = [&val]()->bool {
if (0 == val) {
return true;
} else {
-- val;
return false;
}
};
while (!f()) {
cout << val << endl;
}
}
10.22 重写统计长度小于等于6的单词数量的程序,使用函数代替lambda.
bool isStrShorterThan6(string& str)
{
return str.size() < 6;
}
void test1022()
{
vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red road", "turtle"};
int cnt = count_if(svec.begin(), svec.end(), isStrShorterThan6);
cout << cnt << endl;
}
10.23 bind接受几个参数?
auto newCallable = bind(callable, arg_list);
其中,arg_list是一个用逗号分隔的参数列表,对应给callable的参数。当我们调用newCallable时,实际会调用callable。
arg_list中的参数可能会包含形如_n的名字,其中n是一个整数。这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数位置。
10.24 给定一个string, 使用bind和check_size在一个int的vector中查找第一个大于string长度的值.
using namespace placeholders;
bool check_size(string& str, size_t sz)
{
return str.size()>sz;
}
void test1024()
{
vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red road", "turtle"};
auto iter = find_if(svec.begin(), svec.end(), bind(check_size, _1, 6));
cout << *iter << endl;
}
10.25 在10.3.2节的练习中,编写了一个使用partition的biggies版本.使用check_size和bind重写此函数.
void biggies_25(vector<string>& words, size_t sz)
{
printVec(words);
auto iter = partition(words.begin(), words.end(), bind(check_size, _1, 6));
printVec(words);
}
void test1025()
{
vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "congratulate", "turtle"};
biggies_25(svec, 5);
}
10.26 解释三种插入迭代器的不同之处。
三种迭代器的区别在于元素插入的位置:
- back_inserter:创建一个使用push_back的迭代器。
- front_insert:创建一个使用push_front的迭代器。
- inserter:创建一个使用insert的迭代器。此函数接受第二个参数,元素将被插入到给定迭代器所表示的元素之前。
10.27 处理unique之外,标准库还定义了名为unique_copy的函数,它接受第三个迭代器,表示拷贝不重复元素的目的位置。编写一个程序,使用unique_copy将一个vector中不重复的元素拷贝到一个初始化为空的list中。
void test1027()
{
vector<string> svec = {"the", "quick", "red", "fox", "jumps", "over", "the", "slow", "congratulate", "turtle"};
list<string> slst(15); // 注意list要分配空间,否则没有地方拷贝,程序core掉。
unique_copy(svec.begin(), svec.end(), slst.begin());
for (auto i : slst) {
cout << i << " ";
}
cout << endl;
}
10.28 一个vector中保存1到9,将其拷贝到三个其他容器中。分别使用inserter, back_inserter和front_inserter将元素添加到三个容器中。对每种inserter,估计输出序列是怎样的,运行程序验证你的估计是否正确。
void test1028()
{
vector<int> ivec = {1, 2, 3, 4, 5, 6, 7, 8, 9};
vector<int> ivec_dst1;
list<int> ivec_dst2; // 注意vector没有push_front方法,因此不能用front_inserter.list有该方法。
vector<int> ivec_dst3;
auto iter1 = ivec_dst1.begin();
copy(ivec.begin(), ivec.end(), inserter(ivec_dst1, iter1));
copy(ivec.cbegin(), ivec.cend(), front_inserter(ivec_dst2));
copy(ivec.cbegin(), ivec.cend(), back_inserter(ivec_dst3));
printContainer(ivec_dst1);
printContainer(ivec_dst2);
printContainer(ivec_dst3);
}
10.29 编写程序,使用流迭代器读取一个文本文件,存入一个vector中的string里。
void readByFile(vector<string>& svec, string fileName)
{
ifstream in(fileName);
istream_iterator<string> in_iter(in);
istream_iterator<string> eof;
while (in_iter != eof) {
svec.push_back(*in_iter++);
}
printContainer(svec);
}
void test1029()
{
string fileName = "C:/Users/tutu/Documents/code/cpp_primer/ch10/string.txt";
vector<string> svec;
readByFile(svec, fileName);
}
10.30 使用流迭代器、sort和copy从标准输入读取一个整数序列,将其排序,并将结果写到标准输出。
void test1030()
{
istream_iterator<int> in_iter(cin);
istream_iterator<int> eof;
vector<int> ivec;
ostream_iterator<int> out_iter(cout, " ");
while (in_iter != eof) {
ivec.push_back(*in_iter++);
}
sort(ivec.begin(), ivec.end());
copy(ivec.begin(), ivec.end(), out_iter);
cout << endl;
}
10.31 修改前一题的程序,使其只打印不重复的元素。你的程序应使用unique_copy。
// 将上题中的copy一行改为:
unique_copy(ivec.begin(), ivec.end(), out_iter);
10.32 重写1.6节的书店程序,使用一个vector保存交易记录,使用不同算法完成处理。使用sort和10.3.1节中的compareIsbn函数来排序交易记录,然后使用find和accumulate求和。
void test1031()
{
Sales_item total;
istream_iterator<Sales_item> in_iter(cin);
istream_iterator<Sales_item> eof;
vector<Sales_item> sale_vec;
while (in_iter != eof) {
sale_vec.push_back(*in_iter++);
}
sort(sale_vec.begin(), sale_vec.end(), compareIsbn);
Sales_item sale_sum;
for (auto beg = sale_vec.cbegin(), end = beg; beg != sale_vec.cend(); beg = end) {
end = find_if(beg, sale_vec.cend(), [beg](const Sales_item &item){ return item.isbn() != beg->isbn(); });
cout << accumulate(beg, end, Sales_item(beg->isbn())) << endl;
}
}
// Sales_item类中需要重载"<<" ">>" "+" "+="等符号,才能支持accumulate函数的运算。
10.33 编写程序,接受三个参数:一个输入文件和两个输出文件的文件名。输入文件保存的应该是整数。使用istream_iterator读取输入文件。使用ostream_iterator将奇数写入第一个输出文件,每个值之后都跟一个空格。将偶数写入第二个输出文件,每个值都独占一行。
void readIntByFile(string& in_file, string& out_file_odd, string& out_file_even)
{
ifstream in(in_file);
ofstream out_odd(out_file_odd);
ofstream out_even(out_file_even);
istream_iterator<int> in_iter(in), eof;
ostream_iterator<int> out_iter_odd(out_odd, " "), out_iter_even(out_even, "
");
std::for_each(in_iter, eof, [&out_iter_odd, &out_iter_even](const int i) {
(i%2 ? out_iter_odd : out_iter_even)++ = i;
});
}
void test1033()
{
string inFileName = "C:/Users/tutu/Documents/code/cpp_primer/ch10/int.txt";
string outFileOdd = "C:/Users/tutu/Documents/code/cpp_primer/ch10/odd.txt";
string outFileEven = "C:/Users/tutu/Documents/code/cpp_primer/ch10/even.txt";
readIntByFile(inFileName, outFileOdd, outFileEven);
}
10.34 使用reverse_iterator逆序打印一个vector。
void test1034()
{
vector<int> ivec = {0, 1, 2, 3, 4, 5, 6};
for (auto riter = ivec.crbegin(); riter != ivec.crend(); ++ riter) {
cout << *riter << " ";
}
cout << endl;
}
10.35 使用普通迭代器逆序打印一个vector。
auto iter = ivec.cend();
while (iter != ivec.cbegin()) {
cout << *--iter << " ";
}
cout << endl;
10.36 使用find在一个int的list中查找最后一个值为0的元素。
void test1035()
{
list<int> ilst = {0, 1, 2, 3, 4, 5, 6, 0, 1, 3};
auto find_iter = find (ilst.crbegin(), ilst.crend(), 0);
int dis = distance(find_iter, ilst.crend());
cout << dis << endl;
}
10.37 给定一个包含10个元素的vector,将位置3到7之间的元素按逆序拷贝到一个list中。
vector<int> ivec = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
list<int> dst_lst(10);
copy(ivec.cbegin()+3, ivec.cbegin()+8, dst_lst.rbegin());
for (auto i : dst_lst) {
cout << i << " ";
}
cout << endl;
10.38 列出5个迭代器类别,以及每类迭代器所支持的操作。
- 输入迭代器:只读,不写;单遍扫描,只能递增。支持==、!=、++、*、->
- 输出迭代器:只写,不读;单遍扫描,只能递增。支持++、*
- 前向迭代器:可读写,多边扫描,只能递增。支持输入输出迭代器的所有操作。
- 双向迭代器:可读写,多遍扫描,可递增递减。除支持前向迭代器的操作外,还支持--。
- 随机访问迭代器:可读写,多遍扫描,支持全部迭代器运算。除支持双向操作外,还支持<、<=、>、>=、+、+=、-、-=、迭代器的减法运算符(-)、下标运算符(iter[n], *(iter[n]) )。
10.39 list上的迭代器属于哪类?vector呢?
list的迭代器属于双向迭代器。
vector属于随机访问迭代器。
10.40 你认为copy要求哪类迭代器?reverse和unique呢?
copy前两个参数要求输入迭代器,最后一个参数要求输出迭代器。
reverse要求双向迭代器。
unique要求前向迭代器。
10.41 仅根据算法和参数的名字,描述下面每个标准库算法执行什么操作:
replace(beg, end, old_val, new_val);
replace_if(beg, end, pred, new_val);
replace_copy(beg, end, dest, old_val, new_val);
replace_copy_if(beg, end, dest, pred, new_val);
10.42 使用list代替vector重新实现10.2.3节中的去除重复单词的程序。
void test1042()
{
list<int> ilst = {0, 1, 1,2, 3, 4, 5, 6, 0, 1, 3};
ilst.sort();
ilst.unique();
for (auto i : ilst) {
cout << i << " ";
}
cout << endl;
}