Lambda表达式
lambda介绍:
一个lambda表达式表示一个可调用的代码单元。我们可以将其理解为一个未命名的内联函数。与任何函数类似,一个lambda具有一个返回类型、一个参数列表和一个函数体。但与函数不同,lambda可能定义在函数内部。一个lambda表达式具有如下形式:
[capture list] (parameter list) -> return type { function body }
其中,capture list(捕获列表)是一个lambda所在函数中定义的局部变量的列表(通常为空);parameter list、return type和function body分为表示参数列表、返回类型和函数体。但是,与普通函数不同,lambda必须使用尾置返回来指定返回类型。
我们可以忽略参数列表和返回类型,但是必须永远包含捕获列表和函数体:
auto f = [] { return 42; };
此例中,我们定义了一个可调用对象f,它不接受参数,返回42。
lambda的调用方式与普通函数的调用方式相同,都是使用调用运算符:
cout << f() << endl; // 打印 42
在lambda中忽略括号和参数列表等价于指定一个空参数列表。在此例中,当调用f时,参数列表是空的。如果忽略返回类型,lambda根据函数体中的代码推断出返回类型。如果函数体只是一个return语句,则返回类型从返回的表达式的类型推断而来。否则,返回类型为void。
Note:如果lambda的函数体包含任何单一return语句之外的内容,且未指定返回类型,则返回void。
向lambda传递参数:
与一个普通函数调用类似,调用一个lambda时给定的实参被用来初始化lambda的形参。通常,实参和形参的类型必须匹配。但与普通函数不同的是,lambda不能有默认参数。因此,一个lambda调用的实参数目永远与形参数目相等。一旦形参初始化完毕,就可以执行函数体了。
作为一个带参数的lambda的例子,我们可以编写一个与isShorter函数完成相同功能的lambda:
[](const string &a, const string &b) { return a.size() < b.size(); }
空捕获列表表明此lambda不使用它所在函数中的任何局部变量。lambda的参数与isShorter的参数类似,是const string的引用。lambda的函数体也与isShorter类似,比较其两个参数的size(),并根据两者的相对大小返回一个布尔值。
如下所示,可以使用此lambda来调用stable_sort:
// 按长度排序,长度相同的单词维持字典顺序
stable_sort(words.begin(),words.end(),
[](const strng &a,const string &b) { return a.size() < b.size(); });
当stable_sort需要比较两个元素时,它就会调用给定的这个lambda表达式。
使用捕获列表:
我们现在已经准备好解决原来的问题了——编写一个可以传递给find_if的可调用表达式。我们希望这个表达式能够将输入序列中每个string的长度与biggies函数中的sz参数的值进行比较。
虽然一个lambda可以出现在一个函数中,使用其局部变量,但它只能使用那些明确指明的变量。一个lambda通过将局部变量包含在其捕获列表中来指出将会使用这些变量。捕获列表指引lambda在其内部包含访问局部变量所需的信息。
在本例中,我们的lambda会捕获sz,并只有单一的string参数。其函数体会将string的大小与捕获的sz的值进行比较:
[](const string &a)
{ return a.size() >= sz;};
lambda以一对[]开始,我们可以在其中提供一个以逗号分隔的名字列表,这些名字都是它所在函数中定义的。
由于lambda捕获sz,因此lambda的函数体可以使用sz。
Note:一个lambda只有在其捕获列表中捕获一个它所在函数中的局部变量,才能在函数体中使用该变量。
调用find_if :
使用此lambda,我们可以查找第一个长度大于等于sz的元素:
// 获取一个迭代器,指向第一个满足size() >= sz的元素
auto wc = find_if(words.begin(),words.end(),
[sz](const string &a) { return a.size() >= sz; });
这里对find_if的调用返回一个迭代器,指向第一个长度不小于给定参数sz的元素。如果这样的元素不存在,则返回words.end()的一个拷贝。
我们可以使用find_if返回的迭代器来计算从它开始到words的末尾一共有多少个元素:
// 计算满足size() >= sz的元素的数目
auto count = words.end() - wc;
cout << count << " " << make_plural(count,"word","s") << " of length " << sz << " or longre " << endl;
我们的输出语句调用make_plural来输出“word”或“words”,具体输出哪个取决于大小是否等于1。
for_each算法:
问题的最后一部分是打印words中长度大于等于sz的元素。为了达到这一目的,我们可以使用for_each算法。此算法接受一个可调用对象,并对输入序列中每个元素调用此对象:
// 打印长度大于等于给定值的单词,每个单词后面接一个空格
for_each(wc,words.end(),
[](const string &s) { cout << s << " ";});
cout << endl;
此lambda中的捕获列表为空,但是其函数体还是使用了两个名字:s和cout,前者是它自己的参数。
捕获列表为空,是因为我们只对lambda所在的函数中定义的(非static)变量使用捕获列表。一个lambda可以直接使用定义在当前函数之外的名字。在本例中,cout不是定义在biggies中的局部名字,而是定义在头文件iostream中。因此,只要在biggies出现的作用域中包含了头文件iostream,我们的lambda就可以使用cout。
Note:捕获列表只用于局部非static变量,lambda可以直接使用局部static变量和在它所在函数之外声明的名字。
完整的biggies:
到目前为止,我们已经解决了程序的所有细节,下面就是完整的程序:
1 #include "stdafx.h" 2 #include3 #include 4 #include 5 #include 6 7 using namespace std; 8 9 //比较函数,用来按长度排序10 bool isShorter(const string s1, const string s2)11 {12 return s1.size() < s2.size();13 }14 15 void elimDups(vector &words)16 {17 // 按照字典排序words,以便查找重复单词(重复单词相邻出现)18 sort(words.begin(), words.end());19 // unique算法重排输入范围,使得每个单词只出现一次排列在范围的前部,20 // 返回指向不重复区域之后一个位置的迭代器。21 auto end_unique = unique(words.begin(), words.end());22 // 使用erase删除重复单词23 words.erase(end_unique, words.end());24 }25 26 string make_plural(size_t ctr, const string &word, const string &ending)27 {28 return (ctr > 1) ? word + ending : word;29 }30 31 void biggies(vector &words, vector ::size_type sz)32 {33 elimDups(words);34 //按长度排序,长度相同的单词维持字典序35 stable_sort(words.begin(), words.end(),36 [ ](string const & lhs, string const & rhs)37 {38 return lhs.size() < rhs.size();39 });40 //获取一个迭代器,指向第一个满足size()>= se的元素41 auto wc = find_if(words.begin(), words.end(),42 [sz](const string & a)43 {44 return a.size() >= sz;45 });46 //计算满足size >= sz 的元素的数目47 auto count = words.end() - wc;48 cout << count << " " << make_plural(count, "word", "s")49 << " of length " << sz << " or longer " << endl;50 //打印长度大于等于给定值的单词,每个单词后面接一个空格51 for_each(wc, words.end(),52 [](const string & s)53 {54 cout << s << " ";55 });56 cout << endl;57 }58 59 int main()60 {61 cout << "Enter strings (Ctrl+z to end) : " << endl;62 string word;63 vector words;64 while (cin >> word)65 {66 words.push_back(word);67 }68 69 //vector words = { "the", "quick", "red", "fox", "jumps", "over", "the", "slow", "red", "turtle" };70 vector ::size_type sz = 5;71 biggies(words, sz);72 return 0;73 }