初始C++中的Lambda
在使用泛型编程算法比较序列中的数据的时候,默认情况下,这些算法使用元素类型的<
或=
运算符完成比较,比如sort
,默认使用<
对序列中的元素进行比较,进而完成升序排序。
但是我们可能想用其他的方式进行排序,或者我们所处理序列中的对象没有定义<运算符,比如C++ Primer 5th中自己定义的Sales_data类。这时可能需要我们去做一些定制操作对序列进行操作。
有两种方法进行定制操作:(1) 向算法传递函数;(2) lambda表达式。
lambda
根据算法接受一元谓词还是二元谓词,我们传递给算法的谓词必须严格接受一个或两个参数。但是,有时我们希望进行的操作需要更多参数,超出了算法对谓词的限制。
例一
例如C++ Primer 5th中的练习10.13
练习10.13: 标准库定义了名为partition的算法,它接受一个谓词,对容器内容进行划分,使得谓词为true的值会排在容器的前半部分,而使得谓词为false的值会排在后半部分。算法返回一个迭代器,指向最后一个使谓词为true的元素之后的位置。编写函数,接受一个string,返回一个bool值,指出string是否有5个或更多字符。使用此函数划分words。打印出长度大于等于5的元素。
方法一: 向算法传递函数
template <typename Sequence> void print(Sequence const &seq);
bool cmp(const string &s) {
return s.size() >= 5 ? true : false; // 谓词只能接受string,无法接收划定条件,5是我们自己显示设置的
}
int main(int argc, char **argv)
{
vector<string> v{"Cpp", "Java", "C", "Python", "C#",
"PHP", "JavaScript", "SQL", "Golang"};
print(v);
auto end_part = partition(v.begin(), v.end(), cmp);
auto beg = v.begin();
while (beg != end_part) {
cout << *beg++ << " ";
}
cout << endl;
return 0;
}
该方法可以完成相应的功能,但如果题目中的条件长度大于等于5
更改,我们必须更改return s.size() >= 5 ? true : false;
中的数值。能不能使谓词再接收一个参数sz
可以指定划定条件呢?要使谓词接收多个参数,就需要使用lambda。
方法二: lambda表达式
使用lambda表达式,接收一个参数sz
表示划分条件。
template <typename Sequence> void print(Sequence const &seq);
int main(int argc, char **argv)
{
vector<string> v{"Cpp", "Java", "C", "Python", "C#",
"PHP", "JavaScript", "SQL", "Golang"};
print(v);
size_t sz;
cin >> sz;
auto end_part = partition(v.begin(), v.end(),
[sz] (const string &s) { return s.size() >= sz; } // 捕获变量sz作为划分条件
);
auto beg = v.begin();
while (beg != end_part) {
cout << *beg++ << " ";
}
cout << endl;
return 0;
}
该程序的意义是,使得一个只能接受一个谓词的算法,接受到了两个参数。lambda
还可以捕获更多参数,捕获的方式默认为值捕获
([=]
),其他还有引用捕获
([&]
)。
介绍lambda
一个lambda表达式具有如下形式:
[capture list] (parameter list) -> return type { function body }
- capture list(捕获列表)是一个lambda所在函数中定义的局部变量的列表。lambda只能使用那些明确指明的变量
- 必须使用位置类型返回;
- 可以忽略参数列表和返回类型,但必须永远包含捕获列表和函数体;
- lambda和函数一样使用 调用运算符
()
进行调用; - lambda不能有默认参数;
- 如果lambda的函数体包含任何单一
return
语句之外的内容,且未指定返回类型,则返回void。
例二
求一个序列中大于等于一个给定长度的单词有多少。修改输出,使程序只打印大于等于给定长度的单词。
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
template <typename Sequence> ostream& print(Sequence const &seq);
void elimDups(vector<string> &words);
void biggies(vector<string> &words, vector<string>::size_type sz);
string make_plural(size_t ctr, const string &word, const string &ending = "s");
int main(int argc, char **argv)
{
vector<string> v{"Cpp", "Java", "C", "Python", "C#",
"PHP", "JavaScript", "SQL", "Golang"};
print(v) << endl;
biggies(v, 5); // 将长度大于等于5的字符串按照字典和长度顺序排列
print(v) << endl;
return 0;
}
void biggies(vector<string> &words, vector<string>::size_type sz) { // 传入sz作为划分依据
// 按字典排序,并删除重复字符
elimDups(words);
// 按照字符长度排序,长度相同的字符串保持字典排序(stable)
stable_sort(words.begin(), words.end(),
[] (const string &lhs, const string &rhs)
{ return lhs.size() < rhs.size(); }
);
// 指向第一个长度小于sz的迭代器(wc之前的长度都大于sz)
auto wc = stable_partition(words.begin(), words.end(), // 稳定版划分
[sz] (const string &s) { return s.size() >= sz; } // 捕获sz
);
auto count = wc - words.begin(); // 统计有多少个大于sz的字符串
cout << count << " " << make_plural(count, "word", "s")
<< " of length " << sz << " or longer" << endl;
// 将每一个长度大于sz的字符串都输出
for_each(words.begin(), wc, [] (const string &s) { cout << s << " "; });
cout << endl;
}
void elimDups(vector<string> &words) {
sort(words.begin(), words.end());
auto end_unique = unique(words.begin(), words.end()); // end_unique指向非重复字符的后一位
words.erase(end_unique, words.end()); // 删除end_unique到末尾的重复字符串
}
string make_plural(size_t ctr, const string &word, const string &ending) {
return (ctr < 1) ? word : word + ending;
}
template <typename Sequence> ostream& print(Sequence const &seq) {
for (const auto &i : seq) cout << i << " ";
return cout;
}