跳到主要内容
版本:release

匿名函数表达式

语法

  • parallel? (引用捕获列表1 | 返回类型声明2)? ( 形参声明列表3? ) { 语句列表 }

注释:

  1. 引用捕获列表的语法如下:

    • [ & 标识符 (, & 标识符)* ]
  2. 返回类型声明的语法如下:

  3. 形参声明列表的语法如下:

    • ...?
    • 形参声明4 (, 形参声明)* (, ...)?
  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 实例被调用时,实参会传递给形参。 但是在使用 参数访问表达式 访问实参时:

由于匿名函数表达式的捕获实际上依赖参数传递行为实现,因此以值传递捕获的变量也属于函数的参数,这导致计算预期的 参数位置变得更麻烦,并且位置会伴随捕获的变量改变而改变,因此通常不推荐使用 参数访问表达式 访问实参。

支持默认参数,在声明形参时,在形参的变量名后跟随 = 常量表达式,即可为该形参指定默认值;形参的默认值必须是常量。

带有默认值的形参必须放在参数列表的末尾。

如果形参的类型是 引用容器类型,此时允许使用 parallelreadonly 修饰该形参的类型,表示传入的实参必须 是严格对应 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
}