方法柯里化

lua-users home
wiki

目标:让方法调用 (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 结构,它需要发出将闭包留在堆栈顶部的代码 - 大部分都在上面列出,但需要在之后进行一些清理。此外,它所闭包的实际函数需要存在于常量表中。我们这里有三个选择

最后一个选项可能是最简单的,但可能会导致创建许多相同的函数 - 需要测试这一点


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2008 年 12 月 18 日凌晨 5:12 GMT (差异)