匿名函数表达式
语法
- parallel? (引用捕获列表1 | 返回类型声明2)? ( 形参声明列表3? ) { 语句列表 }
注释:
-
引用捕获列表的语法如下:
- [ & 标识符 (, & 标识符)* ]
-
返回类型声明的语法如下:
- 类型 :
-
形参声明列表的语法如下:
- ...?
- 形参声明4 (, 形参声明)* (, ...)?
-
形参声明的语法如下:
描述
匿名函数表达式用于声明一个匿名的函数,同时产生一个对应的 function 实例。该实例可以被赋值给其他
function 类型的变量,或者作为实参传递给其他函数调用。
function f = (int v) { return v * 2; };
writeln(f(10)); // 输出 20
匿名函数亦属于对象的成员函数。可以使用 parallel 显式地指示匿名函数是 parallel 函数。
如果这么做,GS 保证该表达式创建的 function 实例参数域为 nil;否则:
-
如果匿名函数表达式位于 非
parallel的成员函数作用域内,此时,表达式创建的function实例将把当期域(也就是对象域) 作为实例的参数域。 -
相反的,如果表达式在
parallel的成员函数作用域内,则实例受到实际捕获的变量影响。如果引用捕获任何变量,或者捕获了 RW 的引用容器类型,那么实例的参数域为当前域; 否则,实例的参数域为nil。
被显式声明为 parallel,或者位于 parallel 的成员函数作用域内的匿名函数,其作用域内不能访问对象的 RW 成员。
捕获
匿名函数可以直接在函数体内访问外部作用域的局部变量,这些局部变量将在对应的 function 实例创建时,
被事先被传递(就像传递参数一般)。这一行为被称为 捕获。
GS 默认的捕获方式是 按值捕获,即在 function 实例创建时,捕获的变量值如同被绑定到匿名函数作用域
内的同名局部变量上。实例创建之后,外部变量的值不再影响匿名函数内的同名变量,反之同理。
可以使用 引用捕获列表 说明需要按照引用方 式捕获的变量。被引用方式捕获的变量在匿名函数作用域内外可以
被视为是相同的。发生了引用捕获(无论是直接或是间接的)的匿名函数实例的参数域被限定在当前域。持有引用捕获
变量的匿名函数不能被跨域复制(亦不能执行 make_parallel/make_readonly),如果这么做了,抛出异常。
如果一个被显式声明为 parallel 的匿名函数因为引用捕获,或者捕获了 RW 的引用容器类型 而
导致必须带有参数域,GS 将在运行时进行相关检查并抛出异常。
返回类型
几乎没人使用的语法。另外,因为语法规定的限制,注解了返回类型的匿名函数表达式将无法进行引用捕获。
在匿名函数表达式的参数列表前可以使用 返回类型声明 指定匿名函数的返回类型。如果指定了返回类型,
GS 会在编译期检查函数体内所有的 return 语句 的返回值类型是否与声明的返回类型兼容。如果不兼容,
抛出编译错误。
形参
与具名函数声明类型,匿名函数也可以声明形参,在匿名函数表达式创建的 function 实例被调用时,实参会传递给形参。
但是在使用 参数访问表达式 访问实参时:
由于匿名函数表达式的捕获实际上依赖参数传递行为实现,因此以值传递捕获的变量也属于函数的参数,这导致计算预期的 参数位置变得更麻烦,并且位置会伴随捕获的变量改变而改变,因此通常不推荐使用 参数访问表达式 访问实参。
支持默认参数,在声明形参时,在形参的变量名后跟随 = 常量表达式,即可为该形参指定默认值;形参的默认值必须是常量。
带有默认值的形参必须放在参数列表的末尾。
如果形参的类型是 引用容器类型,此时允许使用 parallel 或 readonly 修饰该形参的类型,表示传入的实参必须
是严格对应 parallel 或 readonly 的。
示例
function foo()
{
map m = {};
return (){return m;};
}
writeln(foo().is_parallel()); // 输出: false
void bar()
{
int x = 10;
function f1 = (int v) { return v * 2 + x; }; // 按值捕获 x
function f2 = [&x](int v) { x += v; return x; }; // 引用捕获 x
writeln(f1.call(10)); // 输出 30
writeln(f2.call(5)); // 输出 15
writeln(f1.call(10)); // 输出 30,f1 内的 x 没有变化
writeln(x); // 输出 15,f2 内的 x 改变了外部的 x
}