两个链接到您提出一个非常重要的假设的解释:即+, - ,*等都是二元函数,也就是说,它们总是只有两个参数。在真正的Lisp中,你可以说(+ 1 2 3 4 5)
来总结一堆数字。但是,如果您愿意接受简单的假设,即每个运营商的身份都是已知的,那么您绝对可以只用一个堆栈来完成此操作。关键是:将堆放倒。
您链接到有这样的堆栈的解释:
底部 - >(+ (- 2 1) 4)
< - 顶部(我们推动并从这里流行)
但是,你也可以阅读你的表达倒退,从权,并建立堆栈是这样的:
顶 - >(+ (- 2 1) 4)
< - 底部
那么你基本上得到RPN,逆波兰式。 RPN在堆叠中的表现非常好。
的算法是这样的:
- 如果你看到一个括号,忽略它
- 如果你看到一个操作数,将其推入堆栈
- 如果你看到一个运营商,调用它。操作员弹出尽可能多的值,然后将其结果推送到堆栈上
取例如(+ (- 2 1) 4)
。下面是该算法将操作:
堆栈:空阅读:)
操作:括号忽略。 左:(+ (- 2 1) 4
堆栈:空阅读:4
操作:操作数压入堆栈。左:(+ (- 2 1)
堆栈:阅读:)
操作:括号忽略。 左:(+ (- 2 1
堆栈:阅读:1
操作:操作数压入堆栈。 左:(+ (- 2
堆栈:读:2
操作:操作数压入堆栈。 左:(+ (-
堆栈:读:-
操作:操作员调用。它会从堆栈中弹出2和1,然后将2-1=1
推到其上。 左:(+ (
堆栈:阅读:(
操作:括号忽略。 左:(+
堆栈:阅读:+
操作:操作者调用。它会从堆栈中弹出1和4,然后将1+4=5
推到其上。 左:(
堆栈:阅读:(
操作:括号忽略。 左:什么
完成!结果如预期的那样是5。
PS关于忽略括号。只要你输入的表达式格式良好,这可以很好地工作,但是它可能会采用非格式表达式,并给它们无意义的值。例如(+ - + 1 2 3 4)
被解释为(+ (- (+ 1 2) 3) 4)
。如果你想防止这种行为,只需将圆括号插入堆栈。当调用操作符时,弹出参数,加上一个额外的标记。 确保该标记是近距离的,否则会抛出错误。一旦你有结果,将其推入堆栈。 确保您读入的下一个标记是开放标记,然后丢弃它。
PPS这一切都变得复杂得多,如果在真实的Lisp中,函数的参数本身可以是函数。然后,您不需要RPN依赖的操作符/操作数之间的便捷区分,并且您需要更多地关注括号作为分组元素。我不知道你是否可以做一个完整的Lisp表达式求值器,它只有一个堆栈,可变参数和函数作为操作数。
来源
2012-10-31 08:11:36
Ord