方法柯里化 |
|
foo:bar()
) 在没有调用部分 (foo:bar
) 的情况下,评估为柯里化的闭包。目前我们有以下语义
function object:wrap(method_name) local f = self[method_name] return function(...) return f(self, ...) end end
但如果我们不用
f = foo:wrap("bar")
而是直接说
f = foo:bar;
Slact 想出了这个主意,我一直在研究解析器,看看实现起来有多容易。
优点:与现有代码完全兼容,并提供了一种使用匿名方法的简单方法,而无需修改现有语法。
缺点:在每次方法调用时创建新的闭包会严重影响性能。应该优化掉这一点,只有在下一个片段不是参数列表(表明我们正在保存它而不是调用它)时才创建闭包。
在纯 Lua 中测试,wrap(foo, 'bar')
和直接调用 nowrap(foo,'bar') -- 对于一个什么也不做的函数 nowrap
之间存在 106% 的速度差异。如果来自纯 Lua 的测试代表了在解析器中编码此功能所带来的性能损失,那么保留当前对方法调用后的参数的检查作为优化可能是有意义的。
备注
-- on changes to the grammar <ToxicFrog> Hmm. I think this changes primaryexp from: prefixexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } to prefixexp { '.' NAME | '[' exp ']' | ':' NAME | funcargs } -- on the emitted code <ToxicFrog> Table at 0 <ToxicFrog> SELF 0 0 <<field>> <ToxicFrog> CLOSURE 2 <<wrapper>> <ToxicFrog> MOVE 0 0 <ToxicFrog> MOVE 0 1 <ToxicFrog> CLOSE 0 <ToxicFrog> And then some stack cleanup. (FIXME: do I fully understand what CLOSE does?) <ToxicFrog> And <<wrapper>> is a function that looks like: <ToxicFrog> GETUPVAL 1 0 <ToxicFrog> GETUPVAL 2 1 <ToxicFrog> VARARG 3 0 <ToxicFrog> TAILCALL <ToxicFrog> 1 0 0 <ToxicFrog> RETURN 0 0 <ToxicFrog> So, the problems that need to be solved: <ToxicFrog> - emitting <<wrapper>> into the generated chunk <ToxicFrog> - making the stack consistent after we generate the closure </pre>
所有有趣的部分都在 lparser.c 中。需要更改 primaryexp 的代码生成。主要需要做两件事:如果我们有 foo:bar 结构,它需要发出将闭包留在堆栈顶部的代码 - 大部分都在上面列出,但需要在之后进行一些清理。此外,它所闭包的实际函数需要存在于常量表中。我们这里有三个选择
最后一个选项可能是最简单的,但可能会导致创建许多相同的函数 - 需要测试这一点