【语法】
一、机制原理
线上事故:表达式计算器宣布支持括号,用户立刻敲出"(2 + 3) * 4",期望 20,界面稳稳当当给出 14——括号被当成普通运算符进了运算符栈,右括号到了无人认领,乘加次序全乱。调度场算法处理括号只需补两条规则:左括号无条件进运算符栈;右括号到来时不断弹出栈顶运算符送进输出,直到弹出左括号本身——这对括号双双消失,中间已经输出的就是括号内的高优先级结果。同时给运算符入栈的弹栈条件补一条护栏:弹到左括号必须停手,左括号是墙,任何运算符都不越墙弹栈。上一轮的基础版只处理加减乘除四则的平铺,这一版补齐括号后,四则混合带任意层嵌套的表达式都能一次转对。
二、错误写法
-- 错误:括号当普通记号进栈,右括号无人处理
for _, tk in ipairs(tokens) do
ops[#ops + 1] = tk
end
三、正确写法
local PRI = {["+"] = 1, ["-"] = 1, ["*"] = 2, ["/"] = 2}
local function toRPN(tokens)
local out, ops = {}, {}
for _, tk in ipairs(tokens) do
if tonumber(tk) then
out[#out + 1] = tonumber(tk)
elseif tk == "(" then
ops[#ops + 1] = tk
elseif tk == ")" then
while ops[#ops] ~= "(" do
out[#out + 1] = table.remove(ops)
end
table.remove(ops)
else
while ops[#ops] ~= nil
and ops[#ops] ~= "("
and PRI[ops[#ops]] >= PRI[tk] do
out[#out + 1] = table.remove(ops)
end
ops[#ops + 1] = tk
end
end
while #ops > 0 do
out[#out + 1] = table.remove(ops)
end
return out
end
local function evalRPN(rpn)
local st = {}
for _, tk in ipairs(rpn) do
if type(tk) == "number" then
st[#st + 1] = tk
else
local b = table.remove(st)
local a = table.remove(st)
if tk == "+" then st[#st + 1] = a + b end
if tk == "-" then st[#st + 1] = a - b end
if tk == "*" then st[#st + 1] = a * b end
if tk == "/" then st[#st + 1] = a / b end
end
end
return st[1]
end
local expr = {"(", "2", "+", "3", ")", "*", "4"}
local label = panel:getChildByName("calcText")
label:setString(tostring(evalRPN(toRPN(expr))))
四、引擎验证
带括号表达式转出的后缀串为 2、3、+、4、,求值得 20;嵌套括号"(1+(23))"转出 1、2、3、*、+,求值得 7;无括号的基础表达式与上一版结果逐项一致。
五、FAQ
问:弹栈遇到左括号为什么停?
答:括号是作用域的墙,墙外的运算符不许越过墙参与排序。
问:右括号处理后栈里还剩什么?
答:只剩墙外的运算符与更早的左括号,流程继续。
问:括号不配对会怎样?
答:右括号弹空栈会取到 nil,入口先做配对校验即可。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【语法算法】 元方法 __len 的触发就这一行: 对带 __len 的表做 t 操作时,Lua 调用 __len 而不是返…
【语法算法】 load 的编译本质就这一行: load 把字符串编译成函数——字符串里的代码被编译成可调用的函数——调用返回…
【语法算法】 error 的传播本质就这一行: error 抛出一个错误——第二个参数指定错误信息的层级——层级 2 表示把…
【语法算法】 pcall 的保护调用本质就这两行: pcall 把函数包在保护壳里执行——函数内部报错不会传播到外层——ok…
【语法算法】 元方法 __tostring 的触发就这一行: 对带 __tostring 的表做 tostring(t) 或…
【语法算法】 元方法 __call 的触发就这一行: 对带 __call 元方法的表做函数调用 t(...) 时,Lua 不…