上一次总结Lambda表达式后,又陆陆续续看到许多关于Lambda表达式的资料,又有了许多新的感触。
1. 更常见的应用场景
在上一篇介绍Lambda表达式的文章中,已经提及Lambda结合sort函数的应用场景,但并未展开。其实,相较于上一篇文章中那个具体的例子,sort函数(find_if函数同理)结合Lambda表达式其实更易于理解:在引入Lambda表达式之前,我们要对容器中的元素进行自定义的排序需要自己定义一个比表函数(也被称为 “谓词”),如下:
bool compare(const string& s1, const string& s2){
return s1.size() < s2.size();
}
vector<string> v{"This","is", "a", "predicate", "."};
sort(v.begin(), v.end(), compare);
for(auto s:v){
cout << s << endl;
}
/*
输出:
.
a
is
This
predicate
*/
在这个例子中,我们定义了一个函数传递给sort算法。这个函数可以重复使用还好,如果只是用使用一次的话就显得比较麻烦。这种情况下可以使用C++11提供的新特性:lamada表达式。代码如下:
vector<string> v{"This","is", "a", "predicate", "."};
sort(v.begin(), v.end(),[](const string& s1, const string& s2){
return s1.size() > s2.size();
});
for(auto s:v){
cout << s << endl;
}
/*
输出:
predicate
This
is
.
a
*/
2. 可变Lambda
在一些资料里还看到了一个有趣的例子:
假设有如下vector,保存的内容是学生的考试成绩,可以用以下代码来寻找第一个及格成绩:
vector<int> score{45, 70, 56, 86, 28, 60, 90};
find_if(score.begin(), score.end(),
[](int v){return (v >=60);});
如果需要找到第n个及格成绩,很自然地会考虑使用下面的代码:
vector<int> score{45, 70, 56, 86, 28, 60, 90};
int counter = 2;
find_if(score.begin(), score.end(),
[counter](int v){
return (v >=60)&&(--counter == 0);
});
但是,这时会出现编译错误,告诉你counter是只读的。其原因是因为在lambda表达式中很少有需要修改捕获值的场景,因此默认捕获值具有const属性。如果出现本例这样,确实希望修改捕获值的情况,C++11使用mutable关键字来解决这个问题。来看完整代码:
vector<int> score{45, 70, 56, 86, 28, 60, 90};
int counter = 2;
auto iter find_if(score.begin(), score.end(),
[counter](int v)mutable{
return (v >=60)&&(--counter == 0);
});
cout << *iter << endl;
需要注意的是,由于是值捕获,处于lambda表达式外面的counter值依然不会改变。如果希望连外面的counter一起修改,使用引用捕获即可。
3. Lambda表达式的递归调用
没想到吧~Lambda表达式也可以递归调用...老套娃了...
直观地想,Lambda表达式由于不存在函数名,因此如何自己调用自己是一个比较棘手的问题。但是,还是有办法的。以阶乘为例:
function<int(int)> factorial =[&](int n){
if(n < 2) return 1;
return n * factorial(n - 1);
};
cout << factorial(3) << endl;
Lambda表达式的递归调用有几个要点:
- 使用标准库中的function模版类型定义表达式类型,其中模版参数与lambda表达式的返回值,参数一致;
- 使用引用捕获来获得factorial的使用权;
- 调用factorial实现递归调用。