C++中的Lambda表达式

1. 引言

最近刷Leetcode经常看discuss,通常是佩服别人算法漂亮。但做第373题Find K Pairs with Smallest Sums时,被这个答案语言上的优雅震惊了。代码片段如下:

auto cmp = [&nums1, &nums2](pair<int, int> a, pair<int, int> b) { 
    return nums1[a.first] + nums2[a.second] > 
             nums1[b.first] + nums2[b.second];
};
priority_queue<pair<int, int>, vector<pair<int, int>>, 
    decltype(cmp)> min_heap(cmp);

通过使用auto、decltype和Lambda表达式等C++ 11新特性,大大压缩了代码量,降低了编写和理解难度,所以决定花时间好好研究一下Lambda表达式。

2. 什么是Lambda表达式?

查Lambda表达式资料时很容易被函数闭包、Lambda演算、形式系统这些深奥名词淹没而放弃学习,其实Lambda表达式就是匿名函数(annoymous function)——允许我们使用一个函数,但不需要给这个函数起名字。还是有点难懂?没关系,看完下面这个例子就清楚了。

int main() {
    vector<int> data;
    for (int i = 0; i < 10; ++i)
        data.push_back(i);
    sort(data.begin(), data.end());
    for (int i = 0; i < data.size(); ++i)
        cout << data[i] << endl;
    return 0;
}

这段代码的含义是初始化data,对data里的元素排序后输出。algorithm库里的sort默认采用升序,想用倒序怎么办呢?对,自己定义一个比较函数cmp,作为参数传给sort:

bool cmp(int &a, int &b);

int main() {
    vector<int> data;
    for (int i = 0; i < 10; ++i)
        data.push_back(i);
    sort(data.begin(), data.end(), cmp);
    for (int i = 0; i < data.size(); ++i)
        cout << data[i] << endl;
    return 0;
}

bool cmp(int &a, int &b) {
    return a > b;
}

在定义了函数bool cmp(int &a, int &b)后,相同的函数签名变得不可用,我不能再用bool cmp(int &a, int &b)这个签名定义一个别的比较函数:

bool cmp(int &a, int &b) {
    return (a % 3) > (b % 3);
}

问题是排序这件事通常不会反复做,那么用cmp比较大小是个一次性的临时需求,排序之后它的任务就已经完成了。所以给它特意起个名字污染命名空间似乎有点不太合算,可不可以不给它起cmp这个名字,又能使用比较大小的功能呢?答案当然是可以的,通过与cmp等价的匿名函数:

int main() {
    vector<int> data;
    for (int i = 0; i < 10; ++i)
        data.push_back(i);
    sort(data.begin(), data.end(), [](int &a, int &b)->bool {
         return a > b;
         });
    for (int i = 0; i < data.size(); ++i)
        cout << data[i] << endl;
    return 0;
}

其中

[](int &a, int &b)->bool {
         return a > b;
}

就是传说中的Lambda表达式了,先不管[]部分,(int &a, int &b)->bool表示接受两个int引用类型的参数,返回值是bool类型,{}里是函数体,是不是很简单?

关于Lambda表达式的意义可以参考知乎上的提问,我自己的理解是Lambda表达式实现了函数名字和功能的分离,允许在不起名字的情况下定义和使用功能。举个生活中的例子,点外卖时我们关心的只是外卖小哥送货上门的“功能”,不需要特意记住他的“名字”。

名字嘛,无所谓

3. 怎么用Lambda表达式?

Lambda表达式的具体语法可以参考cppreference上的Guide。一个Lambda表达式的形式通常为:

[ capture-list ] ( params ) -> ret { body }

其中( params ) -> ret定义了这个匿名函数的参数和返回类型, { body }定义了这个匿名函数的功能,捕捉列表[ capture-list ]是做什么的呢?概括地讲,它使这个匿名函数可以访问外部(父作用域)变量。

还是举个例子:

int main() {
    int a = 0;
    auto f = ([]()->void {cout << a << endl;});
    f();
    return 0;
}

这段代码的含义是定义了一个匿名函数赋给f并运行f,但编译时会报错:
error: 'a' is not captured

因为变量a在函数f的外部,想要访问a的话需要把它加到[ capture-list ]里,也就是:

int main() {
    int a = 0;
    auto f = ([a]()->void {cout << a << endl;});
    f();
    return 0;
}

捕捉方式有按值和按引用两种。比如[a, &b]表示按值捕捉a,按引用捕捉b;[&, a]表示按引用捕捉所有父作用域变量,除了a按值捕捉,[=,&b]表示按值捕捉所有父作用域变量,除了b按引用捕捉。

假设有数组data,想生成只保留data中偶数的新数组result,可以用:

int main() {
    vector<int> data;
    vector<int> result;
    for (int i = 0; i < 10; ++i)
        data.push_back(i);
    for_each(data.begin(), data.end(), [&result](int &elem){
                if (elem % 2 == 0)
                    result.push_back(elem);
             });
    for_each(result.begin(), result.end(), [](int &elem){
             cout << elem << endl;
             });
    return 0;
}

4. 参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容