2.16. Lambda
Lambda 是无名函数,它们通过克隆、复制或引用来捕获本地上下文。 Lambda 比块慢,但在生命周期和捕获模式方面具有更大的灵活性 (见 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,则将自动推断其类型(参阅 Blocks).
2.16.1. 捕获
与数据块不同,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)
}
可以删除 lambda,这会导致对所有捕获的数据调用 Finalizer (参阅 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 的时候
2.16.2. 迭代器
Lambda 是实现自定义迭代器的主要构建块 (参阅 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 的一种更直接的方法是使用生成器 (参阅 Generators).
2.16.3. 实现细节
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<lambda<(extra:int const):int>> (CNT = CNT)
C++ Lambda 类包含用于捕获数据的单个 void 指针:
struct Lambda {
...
char * capture;
...
};
通过引用传递 lambda 的原因在于,当调用 delete 时
为捕获数据调用 Finalizer
通过 NULL 替换捕获
缺少 copy 或 move 可确保没有多个指针指向捕获数据的单个实例。