10.1节练习
练习10.1
问题:头文件algorithm中定义了一个名为count的函数,它类似find, 接受一对迭代器和一个值作为参数。count返回给定值在序列中出现的次数。编写程序,读取int序列存入vector中,打印有多少个元素的值等于给定值
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <list>
using namespace std;
int main() {
vector<int> a={1,2,3,1,1,33,23,55,15,7,9};
cout<<count(a.begin(),a.end(),1);
cout<<endl;
list<string> b={"av","ab","mqt","tv","mv","av"};
cout<<count(b.begin(),b.end(),"av");
return 0;
}
练习10.2
问题:重做上一题,但读取 string 序列存入 list 中
见上一题
10.2.1节练习
练习10.3
问题:用 accumulate求一个 vector<int> 中元素之和
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main() {
vector<int> a={1,2,3,4};
int sum=accumulate(a.begin(),a.end(),0);
cout<<sum<<endl;
return 0;
}
练习10.4
问题:假定 v 是一个vector<double>,那么调用 accumulate(v.cbegin(),v.cend(),0) 有何错误(如果存在的话)
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
int main() {
vector<double> v={1.1,2.2,3,4.4};
cout<<accumulate(v.begin(),v.end(),0);
return 0;
}
会将double类型强制转换成int类型。会忽略小数点的值
练习10.5
问题:在本节对名册(roster)调用equal的例子中,如果两个名册中保存的都是C风格字符串而不是string,会发生什么?
将会比较提供的指针。C风格字符串并没有提供给其运算符比较, 而是定义了函数strcmp。
10.2.2节练习
练习10.6
问题:编写程序,使用 fill_n 将一个序列中的 int 值都设置为0
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
int main() {
list<int> ilist={1,2,3,4,5};
fill_n(ilist.begin(),ilist.size(),0);
for(auto iter:ilist){
cout<<iter<<" ";
}
return 0;
}
练习10.7
下面程序是否有错误?如果有,请改正。
(a) vector<int> vec; list<int> lst; int i;
while (cin >> i)
lst.push_back(i);
copy(lst.cbegin(), lst.cend(), vec.begin());
(b) vector<int> vec;
vec.reserve(10);
fill_n(vec.begin(), 10, 0);
答:a)vec是一个空容器,不能向其中拷贝值。此时如果要拷贝,则要用back_inserter 插入迭代器才可以
b)虽然给vec分配了10的空间,但是里面还是没有元素,所以需要插入迭代器才可以
练习10.8
问题:本节提到过,标准库算法不会改变它们所操作的容器的大小。为什么使用 back_inserter不会使这一断言失效?
因为back_inserter不在标准库算法中,它是一个迭代器。定义在iterator中。
10.2.3节练习
练习10.9
问题:实现你自己的elimDups。分别在读取输入后、调用unique后以及调用erase后打印vector的内容
void elimDups(vector<string> &words){
//先对它排序
sort(words.begin(),words.end());
//unique重排,使得范围内每一项只出现一次
auto end_unique=unique(words.begin(),words.end());
//删除重复项
words.erase(end_unique,words.end());
}
int main() {
vector<string> sv;
//从文件中读取
ifstream input("/Users/jason/CLionProjects/C++p_u10_10.9/input");
if(input.is_open()){
string temp;
while(input>>temp){
sv.push_back(temp);
}
//消除重复项操作
elimDups(sv);
}
input.close();
//打印
for(auto iter:sv){
cout<<iter<<" ";
}
return 0;
}
练习10.10
问题:你认为算法不改变容器大小的原因是什么?
我认为是为了方便实现吧。如果算法要改变容器大小,必然需要对容器进行操作。那么可能会同时要实现算法,还要实现容器的增删改。也会增大程序的耦合度。使得代码独立性变差。
10.3.1节练习
练习10.11
问题:编写程序,使用 stable_sort 和 isShorter 将传递给你的 elimDups 版本的 vector 排序。打印 vector的内容,验证你的程序的正确性
int main() {
vector<string> sv;
ifstream input("/Users/jason/CLionProjects/C++p_u10_10.11/input");
if(input.is_open()){
string temp;
while(input>>temp){
sv.push_back(temp);
}
elimDups(sv);
}
input.close();
stable_sort(sv.begin(),sv.end(),isShorter);
for(auto iter:sv){
cout<<iter<<" ";
}
return 0;
}
在10.9的基础上进行修改。
练习10.12
问题:编写名为 compareIsbn 的函数,比较两个 Sales_data 对象的isbn() 成员。使用这个函数排序一个保存 Sales_data 对象的 vector
bool compareIsbn(const Sales_item &a,const Sales_item &b){
return a.Isbn()<b.Isbn();
}
int main() {
vector<Sales_item> sv{{"cd-mmd"},{"ak-ddl"},{"oj-ssm"},{"tl-shv"}};
sort(sv.begin(),sv.end(),compareIsbn);
for(const auto iter:sv){
cout<<iter.Isbn()<<endl;
}
return 0;
}
练习10.13
问题:标准库定义了名为 partition 的算法,它接受一个谓词,对容器内容进行划分,使得谓词为true 的值会排在容器的前半部分,而使得谓词为 false 的值会排在后半部分。算法返回一个迭代器,指向最后一个使谓词为 true 的元素之后的位置。编写函数,接受一个 string,返回一个 bool 值,指出 string 是否有5个或更多字符。使用此函数划分 words。打印出长度大于等于5的元素
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
bool sizeMoreT5(const string &a){
return a.size()<5;
}
int main() {
vector<string> sv{"aa","fox","red","jumps","over","turtle","increase","abandon","thefox"};
auto iter=partition(sv.begin(),sv.end(),sizeMoreT5);
for(;iter!=sv.end();iter++){
cout<<*iter<<" ";
}
return 0;
}
10.3.2节练习
练习10.14
问题:编写一个lambda,接受两个int,返回它们的和。
auto f=[](int a,int b){return a+b;};
练习10.15
问题:编写一个lambda,捕获它所在函数的int,并接受一个int参数。lambda返回捕获的int和int参数的和。
int a=10;
auto f=[a](int b){return a+b;};
练习10.16
问题:使用 lambda 编写你自己版本的 biggies
了解biggies的功能:
对一个字符向量排序删除重复项求大于给定长度的单词有多少打印大于给定长度的单词
//这里自然可以定义一个isShorter函数,但是不能将sz实时的传入到函数中
//也就是我们输入的谓词,只能对vector中的string进行操作,但是无法和我们输入sz进行比较
void biggies(vector<string> &words,vector<string>::size_type sz){
elimDups(words);//排序并删除重复项
auto iter=partition(words.begin(),words.end(),[sz](string &a){return a.size()<sz;});//获取size>=sz的迭代器
for(;iter!=words.end();iter++){
cout<<*iter<<endl;
}//将size>=sz的打印出来
}
练习10.17
问题:重写10.3.1节练习10.12的程序,在对sort的调用中使用 lambda 来代替函数 compareIsbn
#include <iostream>
#include <vector>
#include <algorithm>
#include "/Users/jason/CLionProjects/C++primer_unit1_class_Sales_item/Sales_item.h"
using namespace std;
int main() {
vector<Sales_item> sv{{"cd-mmd"},{"ak-ddl"},{"oj-ssm"},{"tl-shv"}};
sort(sv.begin(),sv.end(),[](const Sales_item &a,const Sales_item &b){return a.Isbn()<b.Isbn();});
for(auto iter:sv){
cout<<iter<<endl;
}
return 0;
}
练习10.18
问题:重写 biggies,用 partition 代替 find_if。我们在10.3.1节练习10.13中介绍了 partition 算法
见10.16
练习10.19
问题:用 stable_partition 重写前一题的程序,与 stable_sort 类似,在划分后的序列中维持原有元素的顺序
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
void biggies(vector<string> &vs,string::size_type sz){
//对序列进行排序
sort(vs.begin(),vs.end());
//删除重复项
auto end_iter= unique(vs.begin(),vs.end());
vs.erase(end_iter,vs.end());
auto show_iter = stable_partition(vs.begin(),vs.end(),[sz](const string &s){return s.size()<sz;});
for(auto iter=show_iter;iter!=vs.end();iter++){
cout<<*iter<<endl;
}
}
int main() {
vector<string> sv{"aa","fox","red","jumps","over","turtle","increase","abandon","thefox"};
biggies(sv,6);
return 0;
}
10.3.3节练习
练习10.20
问题:标准库定义了一个名为 count_if 的算法。类似 find_if,此函数接受一对迭代器,表示一个输入范围,还接受一个谓词,会对输入范围中每个元素执行。count_if返回一个计数值,表示谓词有多少次为真。使用count_if重写我们程序中统计有多少单词长度超过6的部分
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int length_of_words_over6(vector<string> &vs){
return count_if(vs.begin(),vs.end(),[](const string &a){return a.size()>=6;});
}
int main() {
vector<string> sv{"aa","fox","red","jumps","over","turtle","increase","abandon","thefox"};
cout<<length_of_words_over6(sv)<<endl;
return 0;
}
练习10.21
问题:编写一个 lambda,捕获一个局部 int 变量,并递减变量值,直至它变为0。一旦变量变为0,再调用lambda应该不再递减变量。lambda应该返回一个bool值,指出捕获的变量是否为0
int a=10;
//说明是一次捕获,之后都可以使用它。f()里面拷贝的a一直在变,但是a本身没变
auto f=[a]()mutable ->bool {return (a==0? true:!(--a));};
while(f()==false)
cout<<a<<endl;
上述程序的结果将10输出10遍,因为a本身没有变化。只是拷贝到lambda中的a从10减到了0
auto f2=[&b]{return (b==0? true:!(--b));};
while(!f2())
cout<<b<<endl;
这个程序则是按照题目要求,每次对int值进行修改
10.3.4节练习
练习10.22
问题:重写统计长度小于等于6的单词数量的程序,使用函数代替lambda
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
bool length_6(const string &a){//如果长度小于等于6,就对其计数
return a.size()<=6;
}
//还可以通过将序列排序,然后找到第一个单词不小于6的位置,然后计算大小
//使用函数替代
void length_of_word(const string &a,int &num){
if(a.size()<=6)
num++;
}
int main() {
vector<string> sv{"aa","fox","red","jumps","over","turtle","increase","abandon","thefox"};
int sz=6;
//使用可调用对象直接进行计算
auto num=count_if(sv.begin(),sv.end(),length_6);
auto f=[sz](const string &a){return a.size()>sz;};//size小于6
auto g=[](const string &a,const string &b){return a.size()<b.size();};
//使用lambda
sort(sv.begin(),sv.end(),g);
auto iter=find_if(sv.begin(),sv.end(),f);
int size=iter-sv.begin();//这里为什么不用减去1,因为它总是向后统计一个,比如如果计算字符大于6的个数,就是用sv.end()-iter;sv.end是指向最后一个指针的下一个。
//同样计算小于等于6,比如这里是第八个元素才不小于6的,然后用第八个元素的地址,减去第一个元素的地址,最后就是不大于6的元素个数。相当于8-1
//使用函数
int size2=0;
for_each(sv.begin(),sv.end(),bind(length_of_word,placeholders::_1,ref(size2)));
cout<<"size:"<<size<<endl;
cout<<"size2:"<<size2<<endl;
cout<<num<<endl;
return 0;
}
练习10.23:
问题:bind接受几个参数?
如果函数接受n个参数,bind就接受n+1个参数
练习10.24:
问题:给定一个string,使用 bind 和 check_size 在一个 int 的vector 中查找第一个大于string长度的值
#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <algorithm>
using namespace std;
using namespace placeholders;
bool check_size(const string &a,int &b){
return b>a.size();
}
int main() {
vector<int> vi={1,2,3,4,5,6,7,8,9,10};
string s="ace";
auto iter=find_if(vi.begin(),vi.end(),bind(check_size,ref(s),_1));//这里的_1就是在绑定参数里的位置,而在bind中是按照函数顺序来排列的。
cout<<*iter;
return 0;
}
练习10.25
问题:在10.3.2节的练习中,编写了一个使用partition 的biggies版本。使用 check_size 和 bind 重写此函数
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
using namespace placeholders;
bool bigger(const string &a,string::size_type size){
return a.size()<size;
}
int main() {
vector<string> sv{"aa","fox","red","jumps","over","turtle","increase","abandon","thefox"};
auto iter=partition(sv.begin(),sv.end(),bind(bigger,_1,6));
for(;iter!=sv.end();iter++){
cout<<*iter<<endl;
}
return 0;
}
10.4.1节练习
练习10.26
问题:解释三种插入迭代器的不同之处。
三种迭代器分贝调用了容器的不同成员函数。
front_inserter:调用了push_front
back_inserter:调用了push_back
inserter:调用了insert方法
练习10.27
问题:除了 unique 之外,标准库还定义了名为 unique_copy 的函数,它接受第三个迭代器,表示拷贝不重复元素的目的位置。编写一个程序,使用 unique_copy将一个vector中不重复的元素拷贝到一个初始化为空的list中
#include <iostream>
#include <vector>
#include <algorithm>
#include <list>
using namespace std;
int main() {
vector<string> v_words{"marke","abc","bcd","def","msn","abc","hgk","marke","cc"};
list<string> l_words;
sort(v_words.begin(),v_words.end());
unique_copy(v_words.begin(),v_words.end(),back_inserter(l_words));
for(auto iter=l_words.begin();iter!=l_words.end();iter++){
cout<<*iter<<endl;
}
return 0;
}
练习10.28
问题:一个vector 中保存 1 到 9,将其拷贝到三个其他容器中。分别使用inserter、back_inserter 和 front_inserter 将元素添加到三个容器中。对每种 inserter,估计输出序列是怎样的,运行程序验证你的估计是否正确
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
#include <list>
using namespace std;
int main() {
vector<int> iv={1,2,3,4,5,6,7,8,9};
vector<int> vc1,vc2;
list<int> vl;
copy(iv.begin(),iv.end(),inserter(vc1,vc1.begin()));
copy(iv.begin(),iv.end(),back_inserter(vc2));
copy(iv.begin(),iv.end(),front_inserter(vl));
for(auto i:vc1){cout<<i<<" ";}
cout<<endl;
for(auto i:vc2){cout<<i<<" ";}
cout<<endl;
for(auto i:vl){cout<<i<<" ";}
return 0;
}
注意这里使用头插法,要注意对象容器。像vector就不能使用头插。
10.4.2节练习
练习10.29:
问题:编写程序,使用流迭代器读取一个文本文件,存入一个vector中的string里
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <fstream>
using namespace std;
int main() {
vector<string> vs;
ifstream ifs("/Users/jason/CLionProjects/C++p_u10_10.29/input");
istream_iterator<string> input(ifs),eof;
while(input!=eof){
vs.push_back(*input++);
}
for(auto iter:vs){
cout<<iter<<" ";
}
cout<<endl<<vs.size();
ifs.close();
return 0;
}
练习10.30
问题:使用流迭代器、sort 和 copy 从标准输入读取一个整数序列,将其排序,并将结果写到标准输出
#include <iostream>
#include <iterator>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
istream_iterator<int> input(cin),eof;
ostream_iterator<int> output(cout," ");
vector<int> iv(input,eof);
sort(iv.begin(),iv.end());
copy(iv.begin(),iv.end(),output);
return 0;
}
练习10.31
问题:修改前一题的程序,使其只打印不重复的元素。你的程序应该使用 unique_copy
//上一个题基础上假如这段代码即可
unique_copy(iv.begin(),iv.end(),output);
练习10.32
问题:重写1.6节中的书店程序,使用一个vector保存交易记录,使用不同算法完成处理。使用 sort 和10.3.1节中的 compareIsbn 函数来排序交易记录,然后使用 find 和 accumulate 求和
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <iterator>
#include "/Users/jason/CLionProjects/C++primer_unit1_class_Sales_item/Sales_item.h"
using namespace std;
bool compareIsbn(const Sales_item &a,const Sales_item &b){
return a.Isbn()<b.Isbn();
}
int main() {
ifstream ifs("/Users/jason/CLionProjects/C++p_u10_10.32/input");
istream_iterator<Sales_item> input(ifs),eof;
ostream_iterator<Sales_item> output(cout);
vector<Sales_item> vsi(input,eof);//将数据存入vector中
sort(vsi.begin(),vsi.end(),compareIsbn); //对数据进行排序
//将销售数据序列按照isbn码排列好了之后,此时需要对其求和打印
auto beg=vsi.cbegin();
auto end=beg;
while(beg!=vsi.end()){
end = find_if(beg,vsi.cend(),[beg](const Sales_item &item){return item.Isbn()!=beg->Isbn();});
cout<<accumulate(beg,end,Sales_item(beg->Isbn()))<<endl;//用到了通过string来初始化的点
beg=end;
}
return 0;
}
练习10.33
问题:编写程序,接受三个参数:一个输入文件和两个输出文件的文件名。输入文件保存的应该是整数。使用 istream_iterator 读取输入文件。使用 ostream_iterator 将奇数写入第一个输入文件,每个值后面都跟一个空格。将偶数写入第二个输出文件,每个值都独占一行
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
using namespace std;
int main() {
char *file1="/Users/jason/CLionProjects/C++p_u10_10.33/input";
char *file2="/Users/jason/CLionProjects/C++p_u10_10.33/output_1";
char *file3="/Users/jason/CLionProjects/C++p_u10_10.33/output_2";
ifstream input(file1);
ofstream output_1(file2);
ofstream output_2(file3);
istream_iterator<int> in(input),eof;
ostream_iterator<int> out1(output_1," "),out2(output_2,"\n");
vector<int> iv(in,eof);
auto iter=iv.cbegin(),end=iv.cend();
while(iter!=end){
if((*iter)%2)//奇数
*out1++=*iter;
else
*out2++=*iter;
iter++;
}
input.close();
output_1.close();
output_2.close();
return 0;
}
10.4.3节练习
练习10.34
问题:使用 reverse_iterator 逆序打印一个vector
#include <iostream>
#include <iterator>
#include <vector>
using namespace std;
int main() {
vector<int> vec{1,2,4,5,6,7};
for(auto iter=vec.crbegin();iter!=vec.crend();iter++){
cout<<*iter<<" ";
}
return 0;
}
练习10.35
问题:使用普通迭代器逆序打印一个vector
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec{1,2,34,5,6};
auto iter=vec.cend();
do{
std::cout<<*(--iter)<<" ";
}while(iter!=vec.cbegin());
return 0;
}
练习10.36
问题:使用 find 在一个 int 的list 中查找最后一个值为0的元素
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
int main() {
list<int> il{1,2,3,4,5,0,8,7};
auto iter=find(il.crbegin(),il.crend(),0);
cout<<*iter;
return 0;
}
练习10.37
问题:给定一个包含10 个元素的vector,将位置3到7之间的元素按逆序拷贝到一个list中
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
int main() {
vector<int> a{1,2,3,4,5,6,7,8,9,0};
vector<int> c(a.begin()+2,a.begin()+7);
list<int> b(c.crbegin(),c.crend());
for(auto iter:b){
cout<<iter<<endl;
}
return 0;
}
10.5.1节练习
练习10.38
问题:列出5个迭代器类别,以及每类迭代器所支持的操作
输入迭代器(istream_iterator):==、!=、++、*、->
输出迭代器(ostream_iterator):++、*
前向迭代器(forward_list上的迭代器):==、!=、++、*、->
双向迭代器:==、!=、++、*、->、--
随机访问迭代器:==、!=、++、*、->、--、>、>=、<、<=、+=、-=、+、-、[](iter[n]等价于*iter[n]
练习10.39
问题:list 上的迭代器属于哪类?vector呢?
list:双向迭代器
vector:随机访问迭代器
练习10.40
问题:你认为 copy 要求哪类迭代器?reverse 和 unique 呢?
copy:输入迭代器和输出迭代器
reverse:双向迭代器
unique: 随机访问迭代器
10.5.3节练习
练习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);
1.将范围中值为old_val的替换为new_val
2.将范围中满足条件的值 替换为 new_val
3.将范围中值为old_value的替换为new_val并拷贝到dest中
4.将范围中满足条件的值替换为 new_val并拷贝到dest
10.6节练习
练习10.42
问题:使用 list 代替 vector 重新实现10.2.3节中的去除重复单词的程序
#include <iostream>
#include <list>
using namespace std;
int main() {
list<string> word{"marke","abc","bcd","def","msn","abc","hgk","marke","cc"};
word.sort();
word.unique();
for(auto iter:word){
cout<<iter<<endl;
}
return 0;
}
之前提到,list是直接对容器进行修改的,所以在unique中会直接对数据进行删除,而不像通用unique,会将重复项放到最后,然后返回不重复序列之后位置的迭代器。