据我了解,在Lua 5.2中,环境存储在名为_ENV
的upvalues中。这让我在运行它之前修改了一个块的环境真的很混乱,但在加载它之后。
我想加载一个带有一些函数的文件,并使用chunk将这些函数注入各种环境。例如:
chunk = loadfile( "file" )
-- Inject chunk's definitions
chunk._ENV = someTable -- imaginary syntax
chunk( )
chunk._ENV = someOtherTable
chunk( )
这可能来自Lua吗?我可以找到修改此upvalue的唯一示例是with the C api(另一个example from C api),但我试图在Lua中执行此操作。这可能吗?
编辑:我不确定是否使用调试库接受答案。 docs state函数可能很慢。我这样做是为了提高效率,以便不必从字符串(或文件,甚至更糟)中解析整个块,只是为了将变量定义注入各种环境。
编辑:看起来这是不可能的:Recreating setfenv() in Lua 5.2
编辑:我认为我这样做的最好方法是绑定一个可以修改环境的C函数。虽然这是一种更烦人的方式。
编辑:我认为更自然的方法是将所有块加载到不同的环境中。这些可以通过设置引用块的全局副本的元表来由任何其他环境“继承”。这不需要在加载后进行任何upvalue修改,但仍允许具有这些函数定义的多个环境。
答案 0 :(得分:4)
允许块在不同环境中运行的最简单方法是使其显式并让它接收环境。在块的顶部添加此行可实现此目的:
_ENV=...
现在,您可以随意致电chunk(env1)
及以后chunk(env2)
。
在那里,没有debug
魔法与upvalues。
虽然很明显你的块是否包含该行,但你可以在加载时添加它,通过编写一个合适的读取器函数,该函数首先发送该行,然后发送文件的内容。
答案 1 :(得分:1)
我不明白为什么你要避免使用调试库,而你很乐意使用C函数(在沙箱中也不可能。)
可以使用debug.upvaluejoin
:
function newEnvForChunk(chunk, index)
local newEnv = {}
local function source() return newEnv end
debug.upvaluejoin(chunk, 1, source, 1)
if index then setmetatable(newEnv, {__index=index}) end
return newEnv
end
现在加载任何这样的块:
local myChunk = load "print(x)"
它最初会继承封闭的_ENV
。现在给它一个新的:
local newEnv = newEnvForChunk(myChunk, _ENV)
并为'x'插入一个值:
newEnv.x = 99
现在,当您运行块时,它应该会看到x
的值:
myChunk()
=> 99
答案 2 :(得分:0)
如果您不想修改块(按照LHF的最佳答案),则有两种选择:
function compile(code)
local meta = {}
local env = setmetatable({},meta)
return {meta=meta, f=load('return '..code, nil, nil, env)}
end
function eval(block, scope)
block.meta.__index=scope
return block.f()
end
local block = compile('a + b * c')
print(eval(block, {a=1, b=2, c=3})) --> 7
print(eval(block, {a=2, b=3, c=4})) --> 14
function compile(code)
local env = {}
return {env=env, f=load('return '..code, nil, nil, env)}
end
function eval(block, scope)
for k,_ in pairs(block.env) do block.env[k]=nil end
for k,v in pairs(scope) do block.env[k]=v end
return block.f()
end
local block = compile('a + b * c')
print(eval(block, {a=1, b=2, c=3})) --> 7
print(eval(block, {a=2, b=3, c=4})) --> 14
请注意,如果微优化很重要,则第一个选项的速度大约是_ENV=...
的2%,而第二个选项的速度大约是8–9✕。