编程利器——Lambda表达式

· · 个人记录

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表达式,其余部分就交给编译器吧(黑锅甩的真好)。

比如从大到小排序:

vector<int> vec;
sort(vec.begin(),vec.end(),[](int a,int b){
    return a > b;
});

你也可以将他当做普通函数一般调用,写法与普通函数无甚区别:

auto func = [](){
    cout << "Hello Lambda!" << endl;
};
func();//输出"Hello Lambda"

普通函数是可以访问函数外的变量的,Lambda表达式也可以,修改捕获列表即可。

1.值捕获

值捕获和函数传参中的值传递类似,被捕获变量的值在Lambda表达式创建时通过值拷贝的方式传入,随后对该变量的修改不会影响到Lambda表达式内部的值。

int t = 123;
auto func = [t](){
    cout << t << endl;
};
t = 321;
func();//输出123

需要注意的是:不能修改值捕获变量的值

int t = 123;
auto func = [t](){
    t = 321;
    //Do something
}//错误!Lambda不能修改值捕获变量

如果需要像普通函数一样,允许修改值捕获变量。在函数体前加上mutable关键字即可。即使这样对值捕获变量的修改也不会影响到Lambda表达式之外

int t = 123;
auto func = [t]()mutable{
    t = 321;
    cout << t << endl;
};
func();//输出321
cout << t << endl;//输出123

2.引用捕获

运用值捕获,Lambda表达式内和Lambda表达式外井水不犯河水,互不侵犯。那如果要修改Lambda表达式外的值怎么办?引用捕获来救场了。

int t = 123;
auto func = [&t](){
    cout << t << endl;
};
func();//输出123
t = 321;
func();//输出321
int t = 123;
auto func = [&t](){
    t = 321;
};
cout << t << endl;//输出123
func();
cout << t << endl;//输出321

3.隐式捕获

如果需要捕获的变量太多,懒得写怎么办?很简单,指定一个捕获类型,让编译器来为我们推断需要捕获哪些变量,当然,隐式捕获要么全是值捕获,要么全是引用捕获

int a = 1,b = 2,c = 3;
auto func = [=](){//全为值捕获
    cout << a << " " << b << " " << c << endl;
};
func();//输出1 2 3
int a,b,c;
auto func = [&](){//全为引用捕获
    a = 1;
    b = 2;
    c = 3;
};
func();
cout << a << " " << b << " " << c << endl;//输出1,2,3

4.混合捕获

有些变量需要引用捕获,有些变量需要值捕获,怎么办?让混合捕获来帮你。

[&a,b](){//引用捕获a,值捕获b
    //Do something
}
[&,a](){//a值捕获,其余变量引用捕获
    //Do something
}
[=,&a](){//a引用捕获,其余变量值捕获

}

Lambda表达式的捕获说完了,我们再来看看返回类型。上面的Lambda表达式都没有指定返回类型,却能通过编译。这是因为编译器根据return语句推断出了Lambda表达式的返回类型

考虑这样一种情况:

auto func = [](int a){
    if(a == 1)return 123;
    else return 321.5;
};

本意是想返回一个double值或者一个int值(类型转换),结果编译报错。有多个return语句的情况下,编译器可能无法推断出Lambda表达式的返回类型,这个时候就需要我们自己制定

auto func = [](int a) -> double{//返回一个double值
    if(a == 1)return 123;
    else return 321.5;
};

Lambda表达式看似复杂,却能在许多时候为我们提供不小便利。它也是函数式编程的基石

鸣谢:

本文参考:https://www.cnblogs.com/DswCnblog/p/5629165.html

感谢作者的辛劳付出!