我正在尝试序列化和反序列化Lua闭包
我的基本理解是下面的工厂应该生成闭包(并且Lua没有区分函数和闭包 - 即没有类型'闭包')
> function ffactory(x) return function() return x end end
> f1 = ffactory(5)
> print(f1())
5 <-- so far so good
> s = string.dump(f1)
> f2 = load(s)
> print(f2())
table: 00000000002F7BA0 <-- expected the integer 5
> print(f2()==_ENV)
true <-- definitely didn't expect this!
我希望用f1
序列化整数5。或者,如果string.dump
无法处理闭包,我预计会出错。
我变得非常不同(但我的预期更多)会带来温和的变化。看起来f2
确实是一个闭包,但是string.dump在序列化时并没有尝试序列化x的值。
docs对我帮助不大。 (他们的意思是“......新的上升值”?)
> function ffactory(x) return function() return x+1 end end
> f1 = ffactory(5)
> print(f1())
6 <-- good
> s = string.dump(f1)
> f2 = load(s)
> print(f2())
stdin:1: attempt to perform arithmetic on upvalue 'x' (a table value)
stack traceback:
stdin:1: in function 'f2'
stdin:1: in main chunk
[C]: in ?
答案 0 :(得分:6)
您可以执行以下操作来保存/恢复这些upvalues(请注意,它不处理不同函数之间共享的upvalues):
local function capture(func)
local vars = {}
local i = 1
while true do
local name, value = debug.getupvalue(func, i)
if not name then break end
vars[i] = value
i = i + 1
end
return vars
end
local function restore(func, vars)
for i, value in ipairs(vars) do
debug.setupvalue(func, i, value)
end
end
function ffactory(x) return function() return x end end
local f1 = ffactory(5)
local f2 = (loadstring or load)(string.dump(f1))
restore(f2, capture(f1)) --<-- this restored upvalues from f1 for f2
print(f1(), f2())
这适用于Lua 5.1和Lua 5.2。
如果您稍微更改ffactory
,请注意一个有趣的结果(添加math.abs(0)
;以任何方式使用全局表的任何内容都可以):
function ffactory(x) return function() math.abs(0) return x end end
现在,如果你恢复upvalues会得到相同的结果,但如果你没有恢复upvalues,你会在Lua 5.2下得到一个运行时错误:
lua.exe: upvalues.lua:19: attempt to index upvalue '_ENV' (a nil value)
stack traceback:
upvalues.lua:19: in function 'f2'
upvalues.lua:24: in main chunk
[C]: in ?
答案 1 :(得分:1)
文档很清楚。 string.dump不使用upvalues处理闭包。这是因为upvalues可以是任何东西(包括userdata,Lua如何知道如何序列化?)
upvalues是由于作用域/闭包而在函数本地的外部变量。因为你的例子中的x是ffactory返回的函数的upvalue,所以它不会被序列化。
如果你想以某种方式支持这个,你必须自己存储upvalues并在你反序列化函数后再次设置它们,如下所示:
function ffactory(x)
return function() return x+1 end
end
local f1 = ffactory(5)
print(f1())
local s = string.dump(f1)
f2 = loadstring(s)
debug.setupvalue(f2, 1, 5)
print(f2())