修改 Lua 代码块环境:Lua 5.2
据我所知,在 Lua 5.2 中,环境存储在名为 _ENV 的 upvalue 中。这使得在运行前修改代码块的环境对我来说非常困难,但是在加载后使用它。
我想加载一个带有某些函数的文件,并使用块将这些函数注入到各种环境中。例如:
chunk = loadfile(“file”)
-- 注入块的定义
chunk._ENV = someTable -- 虚构的语法
chunk()
chunk._ENV = someOtherTable
chunk()
从 Lua 中是否可以实现这一点?我能找到的唯一修改此 upvalue 的示例是[使用 C api](https://stackoverflow.com/questions/19167986/how-do-i-set-via-the-lua-c-api-the-environment-table-for-a-chunk-of-lua-code-p)(来自 C api的 [另一个示例](http://lua.2524044.n2.nabble.com/Change-value-of-upvalue-from-within-function-td7642150.html)),但我正在尝试从 Lua 中实现这一点。这是可能的吗?
编辑:我不确定是否接受使用 debug 库的答案。[文档指出](http://www.lua.org/manual/5.2/manual.html#6.10)这些函数可能很慢。我这样做是为了效率,以便不必解析字符串(或文件,更糟糕)中的整个代码块只是为了将变量定义注入到各种环境中。
编辑:看起来这是不可能的:在 Lua 5.2 中重新创建 setfenv()
编辑:我想最好的方法是将 C 函数绑定到可以修改环境的位置。虽然这是一种更麻烦的方式。
编辑:我认为更自然的方法是将所有代码块加载到单独的环境中。这些可以通过设置引用全局复制的块的元表来“继承”任何其他环境。这不需要加载后修改任何 upvalue,但仍允许具有这些函数定义的多个环境。
将代码块在不同环境中运行的最简单方法是显式设置环境变量,让代码块接收环境变量。在代码块顶部添加以下行即可实现:
_ENV=...
现在,您可以自由地调用 chunk(env1),然后是 chunk(env2)。
这样,就不需要通过使用上值的 debug 魔法来实现了。
尽管如果代码块包含该行,它显而易见,但您可以通过编写适当的读取器函数,在加载时添加该行,然后再添加文件内容。
如果您不想修改您的代码块(根据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倍。
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
我不明白为什么你想避免使用 debug 库,但却乐意使用 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)"它最初会继承 enclosing
_ENV。给它一个新的:local newEnv = newEnvForChunk(myChunk, _ENV)并插入一个 'x' 的值:
现在当你运行这个块时,它应该看到
x的值:=>
99