编程利器——Lambda表达式

colazcy

2018-07-20 10:22:17

Personal

Update on Apr.10, 2021: **这篇日报写于2018年(也就是初一暑假),风格比较像 xxs 习作(年龄也确实差不多),受知识水平所限可能内容单薄,请谨慎食用。** 假如遇到一道~~毒瘤~~题,既需要从小到大排序,也需要从大到小排序,甚至还要给自己定义的结构体排序。难道排序函数依次叫做cmp1,cmp2,cmp3?~~太没有逼格了吧~~ 一个**完整的**Lambda表达式由以下几个部分构成: ``` [capture list] (params list) mutable exception-> return type { function body } ``` 各项具体含义如下 1. capture list:捕获外部变量列表 **可以为空,但是不可以省略** 2. params list:形参列表 **可以为空,但是不可以省略** 3. mutable指示符:用来说用是否可以修改捕获的变量 **可以省略** 4. exception:异常设定 **可以省略** 5. return type:返回类型 **可以省略** 6. function body:函数体 **可以为空,但是不可以省略** 太复杂了,对吧? 实际上,在OI中,我们完全用不着将Lambda表达式写完整。我们只需要写出部分Lambda表达式,其余部分就交给编译器吧(~~黑锅甩的真好~~)。 比如从大到小排序: ```cpp vector<int> vec; sort(vec.begin(),vec.end(),[](int a,int b){ return a > b; }); ``` 你也可以将他当做普通函数一般调用,写法与普通函数无甚区别: ```cpp auto func = [](){ cout << "Hello Lambda!" << endl; }; func();//输出"Hello Lambda" ``` 普通函数是可以访问函数外的变量的,Lambda表达式也可以,修改捕获列表即可。 #### 1.值捕获 值捕获和函数传参中的值传递类似,被捕获变量的值在**Lambda表达式创建时**通过值拷贝的方式传入,随后对该变量的修改不会影响到Lambda表达式内部的值。 ```cpp int t = 123; auto func = [t](){ cout << t << endl; }; t = 321; func();//输出123 ``` 需要注意的是:**不能修改值捕获变量的值**。 ```cpp int t = 123; auto func = [t](){ t = 321; //Do something }//错误!Lambda不能修改值捕获变量 ``` 如果需要像普通函数一样,允许修改值捕获变量。在函数体前加上**mutable**关键字即可。即使这样**对值捕获变量的修改也不会影响到Lambda表达式之外** ```cpp int t = 123; auto func = [t]()mutable{ t = 321; cout << t << endl; }; func();//输出321 cout << t << endl;//输出123 ``` #### 2.引用捕获 运用值捕获,Lambda表达式内和Lambda表达式外井水不犯河水,互不侵犯。那如果要修改Lambda表达式外的值怎么办?引用捕获来救场了。 ```cpp int t = 123; auto func = [&t](){ cout << t << endl; }; func();//输出123 t = 321; func();//输出321 ``` ```cpp int t = 123; auto func = [&t](){ t = 321; }; cout << t << endl;//输出123 func(); cout << t << endl;//输出321 ``` #### 3.隐式捕获 如果需要捕获的变量太多,懒得写怎么办?很简单,指定一个捕获类型,让编译器来为我们推断需要捕获哪些变量,当然,**隐式捕获要么全是值捕获,要么全是引用捕获**。 ```cpp int a = 1,b = 2,c = 3; auto func = [=](){//全为值捕获 cout << a << " " << b << " " << c << endl; }; func();//输出1 2 3 ``` ```cpp int a,b,c; auto func = [&](){//全为引用捕获 a = 1; b = 2; c = 3; }; func(); cout << a << " " << b << " " << c << endl;//输出1,2,3 ``` #### 4.混合捕获 有些变量需要引用捕获,有些变量需要值捕获,怎么办?让混合捕获来帮你。 ```cpp [&a,b](){//引用捕获a,值捕获b //Do something } ``` ```cpp [&,a](){//a值捕获,其余变量引用捕获 //Do something } ``` ```cpp [=,&a](){//a引用捕获,其余变量值捕获 } ``` Lambda表达式的捕获说完了,我们再来看看返回类型。上面的Lambda表达式都没有指定返回类型,却能通过编译。这是因为**编译器根据return语句推断出了Lambda表达式的返回类型**。 考虑这样一种情况: ```cpp auto func = [](int a){ if(a == 1)return 123; else return 321.5; }; ``` 本意是想返回一个double值或者一个int值(类型转换),结果编译报错。**有多个return语句的情况下,编译器可能无法推断出Lambda表达式的返回类型,这个时候就需要我们自己制定** ```cpp auto func = [](int a) -> double{//返回一个double值 if(a == 1)return 123; else return 321.5; }; ``` Lambda表达式看似复杂,却能在许多时候为我们提供不小便利。**它也是函数式编程的基石**。 ### 鸣谢: 本文参考:https://www.cnblogs.com/DswCnblog/p/5629165.html 感谢作者的辛劳付出!