【问题标题】:Evaluating expression in Lua in an Environment在环境中评估 Lua 中的表达式
【发布时间】:2023-01-12 01:18:35
【问题描述】:

这个问题对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"))

但是,以下代码不起作用。

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))

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

【问题讨论】:

  • 应该在回答之前要求澄清,但为什么有这些限制?你能直接评估表达式吗? r = sin(0)+cos(1)+2^2?您可以修改表达式以访问您的 tbl (tbl.sin(0)+tbl.cos(1)+2^2) 吗?

标签: lua environment-variables environment evaluation


【解决方案1】:

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 可以使用 debug 库在 Lua 5.2 中复制,因为 Lua 内部将 _ENV 实现为上值,如 shown by 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.sin = math.sin tbl.cos = math.cos function myeval(exp) local prev_env = _ENV _ENV = tbl local result = exp _ENV = prev_env return result end myeval(sin(0))
  • 我想以上述方式使用它。不幸的是它不起作用。
  • 这是我们都提到的问题,您在将任何内容传递给 myeval 之前执行您的表达式。在那一刻,罪是未知的。您可以将其包装到函数中或使用 LMD 显示的第一种方法。
  • 是的。明白了。谢谢。
【解决方案2】:

我假设您不想在将结果作为参数传递之前评估表达式?然后你可以将你的表达式包装到一个函数中,然后被延迟调用。它用 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))

【讨论】:

  • 注意:这是有效的,因为 mathEval 和函数使用相同的 _ENV upvalue,所以为 mathEval 更改它也会为匿名函数更改它。这意味着如果 mathEval 在不同的块中,使用与传递给 mathEval 的匿名函数不同的 _ENV 上值,它可能会失败,例如需要时:require("mathEval"); print(mathEval(function() return sin(42) end))
【解决方案3】:

您正在将 sin(0) 和 sin(0)+cos(1)+2^2 传递给 mathEval(exp) 函数,但是由于 sin 和 cos 不是全局变量并且数学库没有作为环境传递, Lua 无法找到它们。

如果要将数学函数与 mathEval 函数一起使用,可以使用之前创建的 tbl 表来调用数学函数,例如:

print(mathEval("tbl.sin(0)"))
print(mathEval("tbl.sin(0)+tbl.cos(1)+2^2"))

或者您可以将数学库或数学函数作为上值传递给 load() 函数:

function mathEval(exp)
  return load("return " .. tostring(exp), tostring(exp), "t", _ENV)()
end

print(mathEval("math.sin(0)"))
print(mathEval("math.sin(0)+math.cos(1)+2^2"))

可以不加引号来做到这一点,这应该有效:

expression = string.format("%s(%d)", "math.sin", 0)
fn = load( "return " .. expression)
result = fn()
print(result) -- Output: 0

在此示例中,string.format() 函数用于创建字符串“math.sin(0)”。然后像以前一样将生成的字符串传递给 load() 函数。 这样你就不需要在字符串上使用引号,你可以使用占位符来连接变量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-16
    • 2010-11-12
    • 2019-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多