编程利器——Lambda表达式
colazcy
2018-07-20 10:22:17
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
感谢作者的辛劳付出!