从泛型函数开始介绍,泛型函数引出了lambda表达式。
泛型算法
算法永远不会执行容器的操作。
只读算法
find、count这种只会读取其输入范围内的元素,而从不改变元素的算法,就属于制度算法。在积累两个没用过的只读算法。
-
accumulate();
英文翻译为积累函数,其实也就是求和函数。可以用来求和。
accumulate的第三个参数的类型决定了函数中使用哪个加法运算符以及返回值的类型。
-
int sum = accumulate(vec.cbegin(), vec.cend(), 0);
最后一个参数是求和起点,也就是sum一开始为0;
-
string sum = accumulate(v.cbegin(), v.cend(), string(""));
该string初始时为空串,我们通过第三个参数显示的创建了一个string。下面是错误示范
❌string sum = accumulate(v.cbegin(), v.cend(), “”);
错误原因:“”被认为是const char*,它是没有+运算符的,所以便用将会产生编译错误。
-
-
equal();
操作两个序列的算法
-
equal(roster1.cbegin(), roster1.cend(), roster2.cbegin());
会一个一个比较roster1中选定范围的元素和在roster2中以roster2.cbegin()开头的元素。如果所有对应元素都相等,则返回true,反之返回false;
⭐equal已经默认假定第二个序列至少与第一个序列一样长,也就是第二个序列长度大于等于第一个序列的长度。
其实那些只接受一个单一迭代器来表示第二个序列的算法,都假定了第二个序列至少于第一个序列一样长。
-
写容器元素的算法
积累俩基本的
-
fill();
只要传递的前两个参数是有效的范围就很安全
fill(vec.begin(), vec.end(), 0);
-
fill_n();
注意不要越界就好
三个参数与fill有点区别,fill_n规定了需要写的个数
fill_n(vec.begin(), vec.size(), 0);
先稍微记一下一个插入迭代器
-
back_inserter
暂时两个例子就行,而且用此迭代器的空容器也可以使用fill-n。
auto it = back_inserter(v); *it = 42; for(auto c :v) cout << c <<endl;
fill_n(it, 10, 0); for(auto c :v) cout << c <<endl;
拷贝算法
-
copy();
用法类似equal()
auto ret = copy(begin(a1), end(a1), a2);//把a1的内容拷贝给a2;
同样的,保证a2长度大于等于a1的长度。
-
replace_copy();
它之前还有个replace()
replace(ilst.begin(), ilist.end(), 0, 42);
将序列中所有的0都替换成42;
replace_copy()用法稍微复杂,仔细观察例子
replace_copy(ilist.cbegin(), ilist.end(), back_inserter(ivec), 0, 42);
ivec中包含ilist的一份拷贝,不过原来在ilist中值为0的元素在ivec中都变成42。
重排容器元素的算法
上个例子结束
void elimDups(vector<string> &v){
sort(v.begin(), v.end());
auto v_unque = unique(v.begin(), v.end());
v.erase(v_unque, v.end());
}
int main()
{
vector <string>v;
int n;
cin >> n;
for(int i=0; i<n; ++i){
string a;
cin >> a;
v.push_back(a);
}
elimDups(v);
for(auto c : v) cout << c << " ";
}
向算法传递函数
比如sort的第二个版本,可以有第三个参数,这个参数就是谓词。而这个参数对应了一个自己定义的函数,而且规定了sort的谓词属于二元谓词,也就是谓词对应的函数需要两个参数。但是对于有些算法,比如find_if,接受的是一元谓词。但当我们只给谓词对应的函数传一个参数的话是无法满足的,为了解决这个问题,需要使用另外一些语言特性,比如lambda
lambda表达式
-
通常形状
[capture list](parameter list) -> return type {function body}
我们可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体
auto f = []{return 42;}; cout << f(); //返回42
lambda会先根据函数体的代码推断出返回类型,如果函数体只是一个return语句的话,则返回类型从返回的表达式的类型推断而来。否则返回类型为void。
-
注意一下!
lambda不能有默认参数。
-
乍一看好像看不出咋用的。。。
stable_sort(v.begin(), v.end(), [](const string &a, const string &b) { return a.size() < b.size();});
使用捕获列表
-
一个lambda只有在其捕获列表中捕获一个它所在函数中的局部变量,当然它所在的函数是主函数的话也是很可以的。才能在函数体中使用该变量。
即 捕获列表只用于局部非static变量,lambda是可以直接使用局部static变量和它所在函数之外声明的名字。
积累俩算法等下要用
-
find_if()
auto wc = find_if(v.begin(), v.end(), [sz](const string &a) {return a.size()>=sz;} );
上述可以返回一个迭代器,指向v中第一个长度大于等于sz的元素。如果不存在,就返回v.end()的一个拷贝。
-
for_each()
for_each(v.begin(), v.end(), [](const string &a) {cout << a;} );
功能显而易见。
习题示例
练习10.15:编写一个lambda,捕获它所在函数的int,并接受一个int参数。lambda
应该返回捕获的int和int参数的和。
int add(int a){
auto sum = [a](int b){return a+b;};
return sum(5);
}
练习10.16:使用lambda编写你自己版本的biggies。
void elimDups(vector<string> &v){
sort(v.begin(), v.end());
auto v_unque = unique(v.begin(), v.end());
v.erase(v_unque, v.end());
}
bool isShorter(const string &a,const string &b){
return a.size() < b.size();
}
void biggies(vector<string>&words, vector<string>::size_type sz){
elimDups(words);
stable_sort(words.begin(), words.end(),
[](const string &a, const string &b)
{return a.size() < b.size();});
auto wc = find_if(words.begin(), words.end(),
[sz](const string &a)
{return a.size()>=sz;});
auto count = words.end()-wc;
cout << count << " " << "word" << (count>1?"s":"") << " of length "<<sz << " or longer"<<endl;
for_each(wc, words.end(),
[](const string &a)
{cout << a << " ";}
);
cout << endl;
}
int main()
{
vector <string>v;
int n;
cin >> n;
for(int i=0; i<n; ++i){
string a;
cin >> a;
v.push_back(a);
}
biggies(v, 5);
}