文章目录
C++primer-学习心得-第十章-泛型算法
上个章节学习了各种顺序容器,但顺序只定义了很少的操作,我们能做的事情还很少,我们希望能做更多的事情如:查找特定元素、替换或删除一个特定值、重排元素顺序等。标准库没有给每个容器定义相关的成员函数来实现这些操作,但定义了一组泛型算法:算法:实现了一些经典算法的公共接口(如排序和搜索),泛型:可以用于不同类型的元素和多种容器类型(不仅包括标准库类型还包括内置的数组类型),还能用于其他类型的序列。
10.1 概述
大多数算法都定义在头文件algorithm中,标准库还在头文件numeric中定义了一组数值泛型算法。为了说明算法是如何用于不同容器的,我们以find为例,find的工作是在一个未排序的元素序列中查找一个特定元素。概念上find将执行如下步骤。
- 访问序列中的首元素
- 比较此元素与我们要查找的值
- 如果此值与我们要查找的值匹配,find返回表示此元素的值
- 否则,find前进到下一个元素,重复步骤2、3
- 如果到达序列尾,find应停止
- 如果find到达序列末尾,它应该返回一个指出元素未找到的值,这个值的类型应与步骤3中返回值的类型相容。
这些步骤都不依赖容器保存的元素类型,因此只要有一个迭代器可用来访问元素,find就完全不依赖于容器类型。
练习10.1
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int main() {
vector<int> vi = { 1,2,3,2,1,2,3,2,3,22,4,52,3,4,5,67,78,76,6,5,5,4 };
auto test = count(vi.cbegin(), vi.cend(), 2);
cout << test << endl;
}
练习10.2
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
using namespace std;
int main() {
list<string>ls = {"a","a","a","reo","reoreo","b","b"};
auto a=count(ls.cbegin(), ls.cend(), "a");
cout << a << endl;
}
10.2 初识泛型算法
标准库提供了超过一百种算法,有容器类似地这些算法有一致的结构,所以我从理解结构入手更好一点。除了少数例外,标准库算法都对一个范围内的元素(输入范围)进行操作,这个范围通常由一对迭代器表示。通常他们使用范围内的元素的方式是不同的,如是否读取元素,改变元素,重排元素顺序。
1. 只读算法
如前面接触的find和count,不会改变元素。另一个只读算法是accumulate,定义在头文件numeric中。
练习10.3
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
int main() {
vector<int>vi = { 1,2,3,23,23,12,12,23,32,3,423,3,2,21,2,3,4,67,8,9,6 };
auto sum = accumulate(vi.begin(), vi.end(), 0);
cout << sum << endl;
}
另一个只读算法是equal,
equal(r1.cbegin(),r1.cend(),r2.cbegin());
2.写容器元素的算法
如fill将给定值赋予给输入范围的每个元素。fill_n接受一个单迭代器,一个计数值和一个值,将定值赋予给迭代器指向的元素开始的n个值。
back_insert
插入迭代器:向容器中添加元素的迭代器。而back_insert定义在头文件iterator中,它接受一个指向容器的引用返回一个与容器绑定的插入迭代器。
vector<int>vec;
auto it=back_insert(vec);
*it=22;
fill_n(back_insert(vec),10,0);
copy算法
int a[]={0,1,2,3,4,5};
int a2[sizeof(a)/sizeof(*a)];
auto ret=copy(begin(a),end(a),a2);
练习10.6
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
int main() {
vector<int>vi = { 1,2,3,23,23,12,12,23,32,3,423,3,2,21,2,3,4,67,8,9,6 };
fill_n(vi.begin(), vi.size(), 0);
for (auto c : vi) {
cout << c << endl;
}
}
3.重排容器元素的算法
sort,unique
练习10.9
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
void elimdups(vector<int>&vi) {
sort(vi.begin(), vi.end());
auto end_u = unique(vi.begin(), vi.end());
vi.erase(end_u, vi.end());
}
int main() {
vector<int>vi = { 1,2,3,23,23,12,12,23,32,3,423,3,2,21,2,3,4,67,8,9,6 };
elimdups(vi);
for (auto c : vi) {
cout << c << endl;
}
}
10.3定制操作
1.向算法传递函数
谓词:是一个可调用的表达式,其返回结果是一个可用作条件的值。有两类谓词:一元谓词(只接受单一参数)、二元谓词(两个参数)。接受谓词参数的算法对输入序列中的元素调用谓词,因此元素类型必须能转换为谓词的参数类型。如sort接受一个二元谓词参数。(JavaScript也有类似的机制)
bool isShorter(const string &s1,const string &s2){
return s1.size()<s2.size();
}
sort(words.begin(),words.end(),isShorter);
另外还有stable_sort保存等长元素间的字典序。
练习10.11
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
bool isShorter(const int& v1,const int& v2) { return v1 > v2; }//二元谓语
void elimdups(vector<int>&vi) {
sort(vi.begin(), vi.end(),isShorter);
auto end_u = unique(vi.begin(), vi.end());
vi.erase(end_u, vi.end());
}
int main() {
vector<int>vi = { 1,2,3,23,23,12,12,23,32,3,423,3,2,21,2,3,4,67,8,9,6 };
elimdups(vi);
for (auto c : vi) {
cout << c << endl;
}
}
练习10.12
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
class Sales_data {//偷个懒就创建一个简单的Sales_data类
public:
string getisbn() { return bookNo; }
Sales_data(string s, unsigned u, double d) :bookNo(s), sold(u), revenue(d) {}
Sales_data() :Sales_data(" ", 0, 0.0) {}
Sales_data(string s) :Sales_data(s, 0, 0.0) {}
private:
string bookNo;
unsigned sold;
double revenue;
};
bool isShorter(Sales_data& s1, Sales_data& s2) {
return s1.getisbn() > s2.getisbn();
}
void elimdups(vector<Sales_data>&vi) {
sort(vi.begin(), vi.end(),isShorter);
//auto end_u = unique(vi.begin(), vi.end());
//vi.erase(end_u, vi.end());
}
int main() {
vector<Sales_data>vi;
vi.emplace_back("ssss");
vi.emplace_back("asdf", 12, 22);
vi.emplace_back("afasd", 12, 22);
vi.emplace_back("dsfa", 23, 223);
elimdups(vi);
for (auto it = vi.begin(); it != vi.end();it++) {
cout << it->getisbn() << endl;
}
}
练习10.13
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
bool sizes(string s) {
return s.size()<5?true:false;
}
int main() {
vector<string> vs = { "asdf","afdfasd","q3qwer","asd","dfg" };
auto end_p=partition(vs.begin(), vs.end(), sizes);
if (end_p == vs.end()) {
cout << "没有长度大于等于5的字符串" << endl;
}
else {
for (auto it = end_p; it != vs.end(); it++) {
cout << *it << endl;
}
}
}
2.lambda表达式
一个lambda表达式表示一个可调用的代码单元。可以理解未未命名的内联函数。与函数类似,一个lambda具有一个返回类型、一个参数列表和一个函数体。但不同的是它可以定义在函数内部。
[capture list](parameter list)->return type{function body}
练习10.14
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
bool sizes(string s) {
return s.size()<5?true:false;
}
int main() {
int a = 10, b = 20;
auto c = [](int a,int b) {return a + b; };
cout<<c(a,b)<<endl;
}
练习10.15
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
bool sizes(string s) {
return s.size()<5;
}
int main() {
int a = 10, b = 20;
auto c = [a](int b) {return a + b; };
cout<<c(b)<<endl;
}
练习10.17
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
class Sales_data {//偷个懒就创建一个简单的Sales_data类
public:
string getisbn() { return bookNo; }
Sales_data(string s, unsigned u, double d) :bookNo(s), sold(u), revenue(d) {}
Sales_data() :Sales_data(" ", 0, 0.0) {}
Sales_data(string s) :Sales_data(s, 0, 0.0) {}
private:
string bookNo;
unsigned sold;
double revenue;
};
void elimdups(vector<Sales_data>& vi) {
sort(vi.begin(), vi.end(), [](Sales_data& s1, Sales_data& s2) {
return s1.getisbn() > s2.getisbn();
});
//auto end_u = unique(vi.begin(), vi.end());
//vi.erase(end_u, vi.end());
}
int main() {
vector<Sales_data>vi;
vi.emplace_back("ssss");
vi.emplace_back("asdf", 12, 22);
vi.emplace_back("afasd", 12, 22);
vi.emplace_back("dsfa", 23, 223);
elimdups(vi);
for (auto it = vi.begin(); it != vi.end(); it++) {
cout << it->getisbn() << endl;
}
}
练习10.18
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
void elimdups(vector<string>& vi) {
sort(vi.begin(), vi.end());
auto end_u = unique(vi.begin(), vi.end());
vi.erase(end_u, vi.end());
}
void biggies(vector<string>& words, vector<string>::size_type sz) {
elimdups(words);
auto end_p = partition(words.begin(), words.end(), [sz](const string s) {return s.size() < sz; });
for (auto it = end_p; it != words.end(); it++) {
cout << *it << endl;
}
}
int main() {
vector<string>words = { "reoreo","The world","flandre","a","b","sss","sasdf","aaa" };
vector<string>::size_type sz = 6;
biggies(words, sz);
}
练习10.19
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
void elimdups(vector<string>& vi) {
sort(vi.begin(), vi.end());
auto end_u = unique(vi.begin(), vi.end());
vi.erase(end_u, vi.end());
}
void biggies(vector<string>& words, vector<string>::size_type sz) {
elimdups(words);
auto end_p = stable_partition(words.begin(), words.end(), [sz](const string s) {return s.size() < sz; });
for (auto it = end_p; it != words.end(); it++) {
cout << *it << endl;
}
}
int main() {
vector<string>words = { "reoreo","The world","flandre","a","b","sss","sasdf","aaa" };
vector<string>::size_type sz = 6;
biggies(words, sz);
}
3.lambda的捕获和返回
- []
- [names]
- [&]:引用捕获
- [=]:值捕获
- [&,identifier_list]
- [=,identifier_list]
对于一个值被拷贝的变量可以加关键字mutable来允许改变被捕获的值。(值捕获+mutable–>引用捕获)
当我们需要为lambda表达式定义返回类型时必须使用尾置返回类型。
练习10.20
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
void elimdups(vector<string>& vi) {
sort(vi.begin(), vi.end());
auto end_u = unique(vi.begin(), vi.end());
vi.erase(end_u, vi.end());
}
void biggies(vector<string>& words, vector<string>::size_type sz) {
elimdups(words);
auto counts = count_if(words.begin(), words.end(), [sz](const string s) {return s.size() >= sz; });
cout << counts << endl;
}
int main() {
vector<string>words = { "reoreo","The world","flandre","a","b","sss","sasdf","aaa" };
vector<string>::size_type sz = 6;
biggies(words, sz);
}
练习10.21
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
int main() {
int a = 10;
auto f = [&]() {
if (a == 0) {
return true;
}
else {
while (a > 0)
--a;
return false;
}
};
cout << f() << endl;
}
4.参数绑定
在我们这里lambda表达式相比较谓词具有的一个决定性优势就是它可以捕获变量,而谓词定义的时候参数列表是受限的。但如果我们希望能在能多地方使用,lambda表达式的话就需要重复的编写,这时函数就更具有优势了。那么有没有什么办法解决参数捕获的问题呢,方法是有的。
我们可以使用名为bind 的标准库函数,它定义在头文件functional中。一般形式为
auto newCallable=bind(callable,arg_list);
如一元谓词check_size
bool check_size(const string &s,string::size_type sz){
return s.size()>=sz;}
auto wc=find_if(words.begin(),words.end(),bind(check_size,_1,sz));
_n为占位符,定义在名为placeholders的命名空间中,而这个命名空间又定义在std中。可以通过声明:
using namespace std::placeholders;
来使得其中的名字可用。
练习10.22
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
#include<functional>
using namespace std;
using namespace placeholders;
void elimdups(vector<string>& vi) {
sort(vi.begin(), vi.end());
auto end_u = unique(vi.begin(), vi.end());
vi.erase(end_u, vi.end());
}
bool check_size(const string& s, vector<string>::size_type sz) {
return s.size() >= sz;
}
void biggies(vector<string>& words, vector<string>::size_type sz) {
elimdups(words);
auto counts = count_if(words.begin(), words.end(), bind(check_size,_1,sz));
cout << counts << endl;
}
int main() {
vector<string>words = { "reoreo","The world","flandre","a","b","sss","sasdf","aaa" };
vector<string>::size_type sz = 6;
biggies(words, sz);
}
练习10.23
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
#include<functional>
using namespace std;
using namespace placeholders;
bool check_size(const int& i, string s) {
return i >s.size();
}
int main() {
vector<int>vi = { 1,2,3,4,5,6,7,8,9,10 };
string str = "asdfsadf";
auto a = find_if(vi.begin(), vi.end(), bind(check_size, _1, str));
cout << *a << endl;
}
练习10.24
#include<vector>
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
#include<functional>
using namespace std;
using namespace placeholders;
void elimdups(vector<string>& vi) {
sort(vi.begin(), vi.end());
auto end_u = unique(vi.begin(), vi.end());
vi.erase(end_u, vi.end());
}
bool check_size(const string &s,vector<string>::size_type sz) {return s.size() < sz; }
void biggies(vector<string>& words, vector<string>::size_type sz) {
elimdups(words);
auto end_p = partition(words.begin(), words.end(), bind(check_size,_1,sz));
for (auto it = end_p; it != words.end(); it++) {
cout << *it << endl;
}
}
int main() {
vector<string>words = { "reoreo","The world","flandre","a","b","sss","sasdf","aaa" };
vector<string>::size_type sz = 6;
biggies(words, sz);
}
另外要注意的是用bind绑定参数的方式都是以拷贝的形式,相当于值传递的lambda函数,那么如果需要改变绑定的参数时,需要用到标准库函数ref或者cref。
10.4 再探迭代器
迭代器可以分为以下几类:
- 插入迭代器
- 流迭代器
- 反向迭代器
- 移动迭代器
1.插入迭代器
有三种类型的插入迭代器
- back_inserter:创建一个使用push_back的迭代器
- front_inserter:创建一个使用front_back的迭代器
- inserter:创建一个使用insert的迭代器。接受两个参数
练习10.27
#include<vector>
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
#include<functional>
using namespace std;
using namespace placeholders;
int main() {
vector<string>words = { "reoreo","The world","flandre","a","b","sss","sasdf","aaa" ,"aaa","reoreo","reoreo"};
list<string>ls;
unique_copy(words.begin(), words.end(), back_inserter(ls));
vector<string>::size_type sz = 6;
//biggies(words, sz);
for (auto& c : ls)
cout << c << endl;
}
练习10.28
#include<vector>
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
#include<deque>
#include<forward_list>
#include<functional>
#include<iterator>
using namespace std;
using namespace placeholders;
int main() {
vector<int>vi = { 1,2,3,4,5,6,7,8,9 };
list<int>li;
deque<int>di;
forward_list<int>fi;
unique_copy(vi.begin(), vi.end(), back_inserter(li));
unique_copy(vi.begin(), vi.end(), front_inserter(fi));//forward_list没有push_back
unique_copy(vi.begin(), vi.end(), back_inserter(di));
vector<string>::size_type sz = 6;
//biggies(words, sz);
cout << "list" << endl;
for (auto& c : li)
cout << c << endl;
cout << "forward_lsit" << endl;
for (auto& c : fi)
cout << c << endl;
cout << "deque" << endl;
for (auto& c : di)
cout << c << endl;
}
2. iostream 迭代器
istream_iterator读取输入流
- istream_iterator<T> in(is);
- istream_iterator<T>end;
- in1==in2
- in1!=in2
- *in
- in->men
- ++in,in++
ostream_iterator读取输出流
- ostream_iterator<T>out(os)
- ostream_iterator<T>(os,d)
- out=val
- *out,++out,out++
练习10.29
#include<fstream>
#include<string>
#include<vector>
#include<iterator>
#include<iostream>
using namespace std;
int main() {
ifstream is("源.cpp");
istream_iterator<string>in(is);
istream_iterator<string> ends;
vector<string> file_text;
for (auto it = in; it != ends; it++) {
file_text.push_back(*it);
}
is.close();
for (auto c : file_text)
cout << c << endl;
}
练习10.30
#include<fstream>
#include<string>
#include<vector>
#include<iterator>
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
istream_iterator<int>in(cin);
istream_iterator<int> ends;
vector<int> file_text;
for (auto it = in; it != ends; it++) {
file_text.push_back(*it);
}
sort(file_text.begin(), file_text.end());
ostream_iterator<int> out(cout, "\n");
copy(file_text.begin(), file_text.end(), out);
cout << endl;
}
练习10.31
#include<fstream>
#include<string>
#include<vector>
#include<iterator>
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
istream_iterator<int>in(cin);
istream_iterator<int> ends;
vector<int> file_text;
vector<int>ff;
for (auto it = in; it != ends; it++) {
file_text.push_back(*it);
}
sort(file_text.begin(), file_text.end());
auto end_u = unique_copy(file_text.begin(), file_text.end(), back_inserter(ff));
//ff.erase(end_u, ff.end());
ostream_iterator<int> out(cout, "\n");
copy(ff.begin(), ff.end(), out);
cout << endl;
}
练习10.32
#include<fstream>
#include<string>
#include<vector>
#include<iterator>
#include<iostream>
#include<algorithm>
#include<numeric>
using namespace std;
class Sales_data {//偷个懒就创建一个简单的Sales_data类
public:
string getisbn() { return bookNo; }
double getrevenue() { return revenue; }
Sales_data(string s, unsigned u, double d) :bookNo(s), sold(u), revenue(d) {}
Sales_data() :Sales_data(" ", 0, 0.0) {}
Sales_data(string s) :Sales_data(s, 0, 0.0) {}
private:
string bookNo;
unsigned sold;
double revenue;
};
bool isShorter(Sales_data& s1, Sales_data& s2) {
return s1.getisbn() > s2.getisbn();
}
void elimdups(vector<Sales_data>& vi) {
sort(vi.begin(), vi.end(), isShorter);
//auto end_u = unique(vi.begin(), vi.end());
//vi.erase(end_u, vi.end());
}
int main() {
vector<Sales_data>vi;
vi.emplace_back("ssss");
vi.emplace_back("asdf", 12, 22);
vi.emplace_back("afasd", 12, 22);
vi.emplace_back("dsfa", 23, 223);
elimdups(vi);
vector<double>re;
for (auto it = vi.begin(); it != vi.end(); it++) {
//cout << it->getisbn() << endl;
re.push_back(it->getrevenue());
}
auto sum = accumulate(re.begin(), re.end(), 0.0);
cout << sum << endl;
}
练习10.33
#include<fstream>
#include<string>
#include<vector>
#include<iterator>
#include<iostream>
#include<algorithm>
#include<numeric>
#include<fstream>
using namespace std;
void f(string f1, string f2, string f3) {
ifstream in(f1);
istream_iterator<int> ins(in);
istream_iterator<int> endi;
ofstream o1, o2;
o1.open(f2);
o2.open(f3);
ostream_iterator<int>oo1(o1, "\n "),oo2(o2," ");
for (auto it = ins; it != endi; it++) {
cout << *it << endl;
if (*it % 2)
oo2 = *it;
else
oo1 = *it;
}
}
int main() {
ofstream out;
string ff = "num.txt";
out.open(ff);
int i;
ostream_iterator<int> outs(out,"\n");
istream_iterator<int> in(cin);
istream_iterator<int>endi;
while (in != endi) {
*outs++=*in++;
}
out.close();
f(ff, "a.txt", "b.txt");
}
3.反向迭代器
练习10.34
#include<fstream>
#include<string>
#include<vector>
#include<iterator>
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
vector<int> ff = { 1,2,3,4,5,6,7,8,9 };
ostream_iterator<int> out(cout, "\n");
copy(ff.rbegin(), ff.rend(), out);
cout << endl;
}
练习10.35
#include<fstream>
#include<string>
#include<vector>
#include<iterator>
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
vector<int> ff = { 1,2,3,4,5,6,7,8,9 };
vector<int>f;
ostream_iterator<int> out(cout, "\n");
for (auto it : ff)
f.insert(f.begin(), it);
copy(f.begin(), f.end(), out);
cout << endl;
}
练习10.36
#include<fstream>
#include<string>
#include<vector>
#include<iterator>
#include<iostream>
#include<algorithm>
#include <list>
using namespace std;
int main() {
list<int> ff = { 1,2,3,4,5,6,7,8,9,1,2,3 };
auto lasts = find(ff.rbegin(), ff.rend(),0);
if (lasts == ff.rend())
cout << "no value is equal 0";
else
cout << "have" << endl;
}
练习10.37
#include<fstream>
#include<string>
#include<vector>
#include<iterator>
#include<iostream>
#include<algorithm>
#include <list>
using namespace std;
int main() {
vector<int> ff = { 0,1,2,3,4,5,6,7,8,9 };
list<int>f; int i = 0;
for(auto it=ff.cbegin();it!=ff.cend() ;++it,++i)
{
if(i >= 3 && i < 7)
f.push_back(*it);
}
ostream_iterator<int> out(cout, "\n");
copy(f.begin(), f.end(), out);
cout << endl;
}
10.5泛型算法结构
泛型算法所要求的迭代器一共5类
- 输入迭代器:只读,不写,单遍扫描,只能递增
- 输出迭代器:只写,不读,单遍扫描,只能递增
- 前向迭代器:可读写,多遍扫描,只能递增
- 双向迭代器:可读写,多遍扫描,可递增递减
- 随机访问迭代器:可读写,多遍扫描,指出全部迭代器操作
10.6 特定容器算法
list和forward_list成员函数版本的算法
- lst.merge(list2)
- lst.merge(list2,comp)
- lst.remove(val)
- lst.remove_if(pred)
- lst.reverse()
- lst.sort()
- lst.sort(comp)
- lst.unique()
- lst.unique(pred)
练习10.40
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
#include<list>
#include<numeric>
using namespace std;
void elimdups(list<int>& vi) {
vi.sort();
vi.unique();
}
int main() {
list<int>vi = { 1,2,3,23,23,12,12,23,32,3,423,3,2,21,2,3,4,67,8,9,6 };
elimdups(vi);
for (auto c : vi) {
cout << c << endl;
}
}
转载:https://blog.csdn.net/xhh22900/article/details/104503309