Lambda

        • Lambda 表达式探究

          • 基本的语法格式
          • 异同
          • 向lambda 表达式传递参数
          • 使用捕获列表
          • 引用捕获

Lambda 表达式探究

第一次遇到lambda表达式是在学习C++ Primer 时,当时是在STL算法 那个章节,感兴趣的可以自行查阅。今天在学习Cocos2D-X 时再次遇到这个东西。我第一遍接触这个表达式其实很震惊的,后来几经查阅资料,稍微有点明白,但是长时间不用变又忘记了,所以当我再次遇到的时候我决定把它总结一下,加深记忆。

基本的语法格式:

[]()->return_type{};

这个就是lambda 表达式的基本语法结构。看起来很复杂,但是一旦了解了之后就会明白真正复杂的东西只要我们不是自找麻烦,那么用起来还是相对比较简单明了的。

C++Primer 中,我们把这个表达式当作可调用对象。在未接触此表达式之前,我们能接触的可调用表达式一般有三个或两个。常见的是funtion ,Pointer to function 如果你不熟悉类的话你可能不知道class 也是一个可调用对象,只是前提你要重载可调用运算符。今天我们接触的这个也是可以调用对象,而且它们和的关系是比较密切的,在C++ Primer 的某章节中曾经详细介绍过他们之间的关系。这个地方就不搬运了。

上文我们已经提到过了,此表达式同属于可调用对象,那么我们可以类别比对函数的理解来理解lambda 表达式。我曾经看到过如下观点直接把此表达式解释为匿名函数,据说java 中也有类似的存在。一旦我们能把握它的函数性质,理解起来就很容易了。

在来看一下最基本的语法结构。

    [capture_lists](parameters)
    ->return_type{function_body};

[]开始解释。这个方括号里面存在捕获列表。

接下来是() 接下来的和传统函数一直,括号里面可以存放参数列表。

后面是->return_type 尾置返回类型。不详细讲,不了解的可以查资料。

最后面的就更简单的了。是存放我们的函数体。

如果砍去捕获列表的话,同普通函数区别不是很大。只不过是用了尾置返回类型而已,但是这种尾置返回也不算是很新奇的东西。

异同:

1.我们可以在函数体内定义一个lambda 表达式,但是函数是万万不能的,嵌套定义可以大忌。

2.必须使用尾置返回类型,但是普通函数却不是必须的。

示例:

#include<iostream>
int main(){
    auto func=[](){std::cout<<88<<std::endl;};
//Call func ;
    func();
    return 0;
}

我们在main函数内部定义了一个lambda表达式,并且用它初始化了一个func.我们使用了auto 进行自动推断。上面我省略了尾置返回类型。

记住一点,我们可以省略参数列表和尾置返回类型,但是余下的不可省略。

lambda 表达式传递参数:

类似于普通函数,满足实参初始化形参的规则。但是有一点异同,此表达式的形参不能有默认值。

示例:

#include <iostream>
int main(){
    auto func = [](const int& arg){std::cout <<arg<< std::endl; };
    func(13);
    return 0;
}

例子很简单,我们可以看书参数列表中有了参数,同普通函数很相似。

当我们忽略返回类型的时候,如果lambda函数体包含了任何单一return语句之外的内容而且未指定返回类型时,会自动推断为void类型。意思就是说如果只有单一的return语句,那么会根据返回的值进行自动推断。

使用捕获列表:

首先确立一个观点,捕获只能捕获其所在函数体内中的局部变量,并且是不包括static local variable(静态局部比变量)。捕获列表以[]开始,其中可以用逗号分隔开捕获的参数。我们可以省略参数名称,用简单的符号进行替代。

值捕获:

当我们直接把要捕获的参数写到[]中,那么我使用的就是值捕获。

示例:

#include <iostream>
int main(){
    int number = 10;
    auto func = [number](const int arg){std::cout <<arg+number<< std::endl; };
    func(13);
    return 0;
}
引用捕获:

类似我们在函数同学习的形参。所以直接举个例子:

#include <iostream>
int main(){
    int number = 10;
    auto func = [&number](const int arg){std::cout <<arg+number<< std::endl; };
    func(13);
    return 0;
}

对比上面可以发现只是简单的加上了&符号而已。

我们要记住一点。值捕获中被捕获的变量的值是在lambda表达式被创建的时候拷贝,所以后续的修改对其无影响。但是引用使用的一份示例,所以修改会带到此表达式之外。

我们可以返回函数指针类型,那么我也可以返回一个lambda表达式,只是我未曾看过,也未曾写过类似的函数。如果函数返回一个lambda,那么与函数不能返回局部变量的引用类似,此表达式中不能包含引用捕获。

C++ Primer中有如下建议,保持捕获的简单化,避免捕获指针或者引用。然而今天看的Cocos2D-X代码却是违反了这个建议。

最后介绍下隐式捕获,同上面介绍的捕获是一个意思,知识稍微腼腆一点而已。我们不在写上参数名,饿时直接用符号代替

示例:

    auto func = [&](const int arg){std::cout <<arg+number<< std::endl; };
    auto func = [=](const int arg){std::cout << arg + number << std::endl; };

我们使用了&,=符号代替值捕获和引用捕获。需要注意的是混合使用隐式捕获和显式捕获。当我们遇到[&,identifier_list]时。后面的identifier_list不可以使用引用捕获,只能使用值捕获。同样的[=,identifier_list]不可以使用值捕获,只能使用引用捕获。只是一些规则而已。

可变lambda:

当我们使用值捕获的时候,通常是不需要修改捕获的参数,但是想修改的时候记得加上mutable关键字。

示例:

    auto func = [=](const int& arg) mutable {std::cout << arg + number << std::endl; };
    May 4, 2015 6:18 PM By Max

我发现CSDN的MarkDown不是很好用,所以我把源文件分享下,可以下载下来看,我使用的是Haroopad,挺好用的编辑器。

源文件下载地址:戳我下载源文件

时间: 2024-10-19 22:38:57

Lambda的相关文章

Lambda表达式实战视频教程

视频教程地址: https://my.oschina.net/u/3217554/blog/1507456 1:Lambda表达式及函数式接口介绍 2:Lambda表达式详解 3:方法的引用(一) 4:方法的引用(二) 5:Stream API(一) 6:Stream API(二) 7:Lambda表达式.方法的引用.Stream API实战

lambda表达式封装对数据库的查询

前言: 1.为什么要封装lambda表达式数据库查询,原因有一下几点: 1.1.在以往的开发中进行数据库表查询时,其实所需要的字段就是其中几个,但是在开发中,开发者往往习惯select * 进行查询,当数据多和用户量多时,查询的效率会降低. 1.2.在写查询where条件的时候,总是用string.format去拼接字符串,开发效率低. 1.3.代码不够优雅,代码中嵌套和多sql语句,如果是表字段发生改变时编译器检查不出来,代码出错的概率大. 1.4.本着 write less  do more

Lambda表达式

import org.junit.Test; import java.util.Comparator; import java.util.function.Consumer; /** * 一.Lambda 表达式基础语法:Java8中引入一个新的操作符"->"该操作符称为箭头操作符或Lambda操作符 * 箭头操作符将Lambda表达式拆分为两部分: * 左侧: Lambda表达式的参数列表 * 右侧: Lambda表达式中所需要执行的功能,即Lambda体 * * 语法格式一:

几种查询方法(lambda Linq Enumerable静态类方式)

1.需要一个数据源类: using System; using System.Collections.Generic; namespace Linq { public class Student { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } public class Data { public static List<Student> studentLi

一分钟搞明白java8中的lambda

项目结构是这样的 User是一个普通的pojo类 UserCompare是一个实现了Comprator的类 现在我们有一个需求:给一个user组成的list 按照user的年龄排序.实现不难,代码如下: 这种方法由于sort方法的第二个参数是Comparator 所以你要写一个实现类(我这里是UserCompare类),并且override该接口的实现方法. java8提供了lambda来简化,有了lambda程序员从此不加班呀- 刚才那个Comparator的实现类以及内部若干代码就都省了,代

python笔记-lambda函数、sorted函数、map函数

1.lambda函数:又称匿名函数,示例如下: def f(x): return x**2 print f(4)  #16 g = lambda x:x**2 print g(4)  #16 2.map函数 print map(lambda x:x**2,range(10)) #[0, 1, 4, 9, 16, 25, 36, 49, 64, 81] 3.sorted函数 dict = {9:2,4:3,6:9,'a':'test','e':'fff','*':'$'} print sorted

C#学习日记25---匿名方法 与 Func委托 与 lambda表达式

       在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法.C# 2.0 引入了匿名方法(委托),而在 C# 3.0 及更高版本中,Lambda 表达式取代了匿名方法,作为编写内联代码的首选方式. 匿名委托(方法): 匿名委托的叫法并不准确,准确的应该叫做匿名方法,(总之两者是一个意思啦).前面  委托类型  中我已经提到过,委托是用于引用与其具有相同标签的方法.换句话说,您可以使用委托对象调用可由委托引用的方法(参数是方法名).而匿名方法则是将代码块作为委托参数(参数是实

java8之lambda表达式(构造器引用)

构造器引用同方法引用类似,不同的是在构造器引用中方法名是new.例如,Button::new表示Button类的构造器引用.对于拥有多个构造器的类,选择使用哪个构造器取决于上下文.假设你有一个字符串列表,并且希望调用Button类的构造器使用列表中的字符串来构造一个按钮列表,可以使用如下表达式: List<String> labels = ....; Stream<Button> stream = labels.stream().map(Button::new); List<

lambda表达式+python内置函数

传统的定义函数方式如下 def f1(): return 123 lambda表达式定义函数 f2 = lambda : 123 python3的内置函数 1.abs 绝对值 i = abs(-11) print (i) 输出结果是11 abs = absolute 2,all 循环参数,如果每个元素都为真,则返回为真 r = all([True, True]) print (r) 在python中 0 () [] ''和 None是False(空值都是假的) r = all(["123&quo

兰姆达表达式Lambda 表达式(C# 编程指南)

转https://msdn.microsoft.com/zh-cn/library/bb397687.aspx Lambda 表达式是一种可用于创建委托或表达式目录树类型的匿名函数.通过使用 lambda 表达式,可以写入可作为参数传递或作为函数调用值返回的本地函数.Lambda 表达式对于编写 LINQ 查询表达式特别有用. 若要创建 Lambda 表达式,需要在 Lambda 运算符 => 左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块.例如,lambda 表达式 x => x