Этот вопрос имеет некоторую отсылку к вопросу Оценка выражения в 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))
Я хочу оценивать выражения без использования кавычек. Как это сделать?





Вы передаете 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(), как и раньше. Таким образом, вам не нужно использовать кавычки в строке, и вы можете использовать заполнители для объединения переменных.
Я предполагаю, что вы не хотите оценивать выражение перед передачей результата в качестве аргумента? Затем вы можете обернуть свое выражение в функцию, которая затем лениво вызывается. Он заменяет среду на 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 находится в другом фрагменте, используя другое значение _ENV upvalue, чем анонимная функция, переданная mathEval, например. когда это требуется: require("mathEval"); print(mathEval(function() return sin(42) end))
Проблема со строкой print(mathEval(sin(0)+cos(1)+2^2)) заключается в том, что аргумент mathEval оценивается до запуска mathEval, поэтому вычисление переменных sin и cos нельзя отложить до среды 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; к сожалению, это было удалено в пользу _ENV в Lua 5.2 и более поздних версиях. Затем код становится тривиальным:
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.sin = math.sin tbl.cos = math.cos function myeval(exp) local prev_env = _ENV _ENV = tbl local result = exp _ENV = prev_env вернуть результат end myeval(sin(0))
Я хочу использовать его вышеописанным способом. К сожалению, это не работает.
Это проблема, о которой мы все упоминали, вы выполняете свое выражение ДО того, как что-либо передаете myeval. В этот момент грех не известен. Вы можете либо обернуть его в функцию, либо использовать первый подход, показанный LMD.
Надо было уточнить перед ответом, но зачем такие ограничения? Можете ли вы оценить выражение напрямую, например.
r = sin(0)+cos(1)+2^2? Можете ли вы изменить выражение для доступа к вашей таблице (tbl.sin(0)+tbl.cos(1)+2^2)?