.. _lambdas: ====== Lambda ====== Lambda 是无名函数,它们通过克隆、复制或引用来捕获本地上下文。 Lambda 比块慢,但在生命周期和捕获模式方面具有更大的灵活性 (见 :ref:`Blocks `). lambda 类型可以使用类似函数的语法进行声明:: lambda_type ::= lambda { optional_lambda_type } optional_lambda_type ::= < { optional_lambda_arguments } { : return_type } > optional_lambda_arguments := ( lambda_argument_list ) lambda_argument_list := argument_name : type | lambda_argument_list ; argument_name : type lambda < (arg1:int;arg2:float&):bool > Lambda 可以是局部变量或全局变量,并且可以通过引用作为参数传递。 Lambda 可以移动,但不能复制或克隆:: def foo ( x : lambda < (arg1:int;arg2:float&):bool > ) { ... var y <- x ... } Lambda 可以通过 ``invoke`` 或类似调用的语法来调用:: def inv13 ( x : lambda < (arg1:int):int > ) { return invoke(x,13) } def inv14 ( x : lambda < (arg1:int):int > ) { return x(14) } Lambda 表达式通常通过 move 语法声明:: var CNT = 0 let counter <- @ (extra:int) : int { return CNT++ + extra } let t = invoke(counter,13) lambda 和 block 声明之间有很多相似之处。 主要区别在于块是用 ``$`` 符号指定的,而 lambda 是用 ``@`` 符号指定的。 lambda 表达式也可以通过内联语法进行声明。 对于仅包含 return expression 的 lambda,也有类似的简化语法。 如果在泛型 或 函数中充分指定了 lambda,则将自动推断其类型(参阅 :ref:`Blocks `). ------- 捕获 ------- 与数据块不同,lambda 可以显式指定其捕获类型。有几种可用的捕获类型: * 通过复制 * 通过移动 * 按克隆 * 按引用 通过引用捕获需要 unsafe。 默认情况下,将生成 capture by copy。如果 copy 不可用,则默认的 mobile 捕获需要 unsafe:: var a1 <- [1,2] var a2 <- [1,2] var a3 <- [1,2] unsafe { // 通过引用捕获 a1 所需的 dO var lam <- @ capture(ref(a1),move(a2),clone(a3)) { push(a1,1) push(a2,1) push(a3,1) } invoke(lam) } .. _lambdas_finalizer: 可以删除 lambda,这会导致对所有捕获的数据调用 Finalizer (参阅 :ref:`Finalizers `):: delete lam Lambda 可以指定在默认终结器之前调用的自定义终结器:: var CNT = 0 var counter <- @ (extra:int) : int { return CNT++ + extra } finally { print("CNT = {CNT}\n") } var x = invoke(counter,13) delete counter // 这是调用 Finalizer 的时候 .. _lambdas_iterator: --------- 迭代器 --------- Lambda 是实现自定义迭代器的主要构建块 (参阅 :ref:`Iterators `). Lambda 可以通过 ``each`` 或 ``each_ref`` 函数转换为迭代器:: var count = 0 let lam <- @ (var a:int &) : bool { if ( count < 10 ) { a = count++ return true } else { return false } } for ( x,tx in each(lam),range(0,10) ) { assert(x==tx) } 要用作迭代器,lambda 必须 * 具有一个参数,这是每个步骤迭代的结果 * 具有布尔返回类型,其中 ``true`` 表示继续迭代,``false`` 表示停止 制作 iterator 的一种更直接的方法是使用生成器 (参阅 :ref:`Generators `). ---------------------- 实现细节 ---------------------- Lambda 是通过为捕获创建无名结构以及为 lambda 正文创建函数来实现的。 让我们看一个包含单个捕获变量的示例:: var CNT = 0 let counter <- @ (extra:int) : int { return CNT++ + extra } Daslang 将生成以下代码: 捕获结构:: struct _lambda_thismodule_7_8_1 { __lambda : function<(__this:_lambda_thismodule_7_8_1;extra:int const):int> = @@_lambda_thismodule_7_8_1`function __finalize : function<(__this:_lambda_thismodule_7_8_1? -const):void> = @@_lambda_thismodule_7_8_1`finalizer CNT : int } 主体函数:: def _lambda_thismodule_7_8_1`function ( var __this:_lambda_thismodule_7_8_1; extra:int const ) : int { with ( __this ) { return CNT++ + extra } } 终结器函数:: def _lambda_thismodule_7_8_1`finalizer ( var __this:_lambda_thismodule_7_8_1? explicit ) { delete *this delete __this } Lambda 创建被捕获结构的升序所取代:: let counter:lambda<(extra:int const):int> const <- new> (CNT = CNT) C++ Lambda 类包含用于捕获数据的单个 void 指针:: struct Lambda { ... char * capture; ... }; 通过引用传递 lambda 的原因在于,当调用 delete 时 1. 为捕获数据调用 Finalizer 2. 通过 NULL 替换捕获 缺少 copy 或 move 可确保没有多个指针指向捕获数据的单个实例。