在环境中评估Lua中的表达式



这个问题对数学环境中Lua求表达式有一定的参考意义下面的代码可以工作。

tbl = {}
tbl.sin = math.sin
tbl.cos = math.cos
function mathEval(exp)
return load("return " .. exp, exp, "t", tbl)()
end
print(mathEval("sin(0)"))
print(mathEval("sin(0)+cos(1)+2^2"))

但是,下面的代码不能工作。

tbl = {}
tbl.sin = math.sin
tbl.cos = math.cos
function mathEval(exp)
return load("return " .. tostring(exp), tostring(exp), "t", tbl)()
end
print(mathEval(sin(0)))
print(mathEval(sin(0)+cos(1)+2^2))

我想在不使用引号的情况下计算表达式。怎么才能做到呢?

print(mathEval(sin(0)+cos(1)+2^2))的问题在于mathEval的参数在mathEval运行之前已经求值,因此不能将变量sincos的求值推迟到mathEval的环境中;也就是说,mathEval获得了一个值,但根本没有要计算的表达式!

首先,在不使用mathEval的情况下计算这些数学表达式的一种选择是简单地临时更改您的环境:
local prev_env = _ENV -- this is needed to restore the environment later on
_ENV = tbl -- enter custom environment
local result = sin(0)+cos(1)+2^2
_ENV = prev_env -- restore environment
print(result)

如果你想让mathEval作为一个方便的帮助器,你必须将表达式作为一个函数传递,将值返回给表达式,这样调用函数将对表达式求值;这允许您延迟初始化。你必须使用一个叫做setfenv的功能强大的函数,它允许你改变func的环境;不幸的是,在Lua 5.2和以后的版本中,为了支持_ENV而删除了这个功能。这样代码就变得微不足道了:

local function mathEval(func)
setfenv(func, tbl)
return func
end
mathEval(function() return sin(0)+cos(1)+2^2 end)

setfenv可以在Lua 5.2中使用debug库复制,因为Lua内部实现了_ENV作为上值,如Leafo所示:

local function setfenv(fn, env)
local i = 1
while true do
local name = debug.getupvalue(fn, i)
if name == "_ENV" then
debug.upvaluejoin(fn, i, (function()
return env
end), 1)
break
elseif not name then
break
end
i = i + 1
end
return fn
end

我假设您不想在将结果作为参数传递之前评估表达式?然后可以将表达式包装成一个函数,然后惰性调用该函数。它将环境替换为tbl,执行该函数,并恢复环境。

tbl = {}
tbl.sin = math.sin
tbl.cos = math.cos
function mathEval(func)
local old = _ENV
_ENV = tbl
local r = func()
_ENV = old
return r
end
print(mathEval(function() return sin(0)+cos(1)+2^2 end))

最新更新