MCFPP设计——Lambda
语法设计
采用for-in Expression and Lambda Expression 未来的Lambda表达式设计 · MinecraftFunctionPlusPlus/MCFPP · Discussion #33中的设计,敲定Lambda语法如下:
func test(Func<int,int> process){ dynamic int a = 10; print(a); a = process(a); print(a); } func lamb(x){ return x + 1; } func main(){ test((x){return x + 1;}); # OK test(x){return x + 1;} # OK test(lamb); # OK var qwq = lamb; test(qwq); # OK }
lambda在MCFPP对应两种类型:Func<>
类型和Action<>
类型。前者表示一个拥有返回值的lambda,其泛型参数列表的最后一个是返回值,其余是lambda的参数。例如Func<int, int>
表示一个返回值为int
,同时接收一个int
类型作为参数的lambda。而Action
表示没有返回值的lambda,其泛型参数列表就表示lambda接收的参数类型。例如Action<int, int>
表示接收两个int
类型作为参数,没有返回值的lambda。
实现
无论是Func
还是Action
,在MCFPP中的实现方式都是类似的。变量储存方面,lambda类型将会被储存为一个NBT复合标签。假设我们有一个如下的Lambda:
func main(){ int x = 50; int y = 50; test { print(x); print(y); } } func test(Action action){ action(); }
结构如下:
{ %id: "default:lambda_1", //mcfunction函数的命名空间ID x: 50, //捕获到的外部函数的x值 y: 50 //捕获到的外部函数的y值 }
譬如在执行到test
函数的时候,首先构造lambda。注意此时编译已经完成,我们讨论的是执行的过程。首先将要捕获的x
,y
变量传入lambda NBT中,也就是上面NBT中描述的。随后,将整个lambda NBT作为参数传递给test方法。随后,根据action中的内容,调用对应的lambda函数,同时把lambda NBT中的变量传入lambda函数的栈中。在调用完毕后,还需要将栈中被修改过的lambda NBT中的变量传回lambda NBT,从而更新修改。
对于编译器来说,已知的Lambda变量和未知的Lambda变量的唯一区别,就是Lambda中包含的函数是否是已知的。对于未知的变量来说,将会用宏的方式,使用lambda NBT中的%id
字段调用函数,而对于已知的变量来说,则会直接使用function
命令调用函数。