评估环境中 Lua 的表达式

mat*_*uty 3 environment evaluation lua environment-variables

这个问题有一些参考问题Evaluating expression in Lua in Mathematics Environment 下面的代码有效。

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"))
Run Code Online (Sandbox Code Playgroud)

但是,下面的代码不起作用。

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))
Run Code Online (Sandbox Code Playgroud)

我想在不使用引号的情况下计算表达式。那怎么办呢?

LMD*_*LMD 6

该行的问题print(mathEval(sin(0)+cos(1)+2^2))在于, 的参数在运行之前mathEval进行评估,因此评估变量并且不能推迟到;的环境中。也就是说,获取一个值,并且根本不需要计算表达式! mathEvalsincosmathEvalmathEval

首先,在不使用的情况下评估此类数学表达式的一种选择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)
Run Code Online (Sandbox Code Playgroud)

如果你想mathEval作为一个方便的助手,你必须将表达式作为函数传递,将值返回给表达式,这样调用函数就会计算表达式;这允许您推迟初始化。您必须使用一个名为 的强大函数setfenv,它允许您更改 ; 的环境func。不幸的是,这个_ENV在 Lua 5.2 及更高版本中被删除了。那么代码就变得很简单:

local function mathEval(func)
    setfenv(func, tbl)
    return func
end
mathEval(function() return sin(0)+cos(1)+2^2 end)
Run Code Online (Sandbox Code Playgroud)

setfenv可以使用该debug库在 Lua 5.2 中复制,因为 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
Run Code Online (Sandbox Code Playgroud)