上一篇 文章讲了如何通过正则来将输入的表达式解析为多个 Token,而这篇文章的核心在于如何对 表达式求值。
我们输入的表达式,即我们通常见到的表达式,都是中缀表达式 —— 中缀的含义是,在表达式中,运算符是放中间的,比如 (1 + 2) * 3,运算符都是在数的中间。然而在计算机的世界里,还存在着前缀表达式和后缀表达式 —— 由名字也很容易知道,前缀表达式是将运算符放在数之前,后缀表达式是将运算符放到数之后。
表达式
形式
中缀
1 + (3 - 2) * 4 / 5
前缀
+ 1 / * - 3 2 4 5
后缀
1 3 2 - 4 * 5 / +
中缀表达式的劣势在于,一旦表达式复杂化,比如多层括号嵌套同时还要注意运算符的优先级,那么要编写计算中缀表达式的值的代码也十分的复杂。而对于前缀表达式和后缀表达式的计算,则十分的简单。
以后缀表达式为例:
从左往右扫描表达式,如果遇到数,那么将数入栈
如果遇到运算符,那么从栈中依次弹出两个数 n1 和 n2,使用该运算符对这两个数进行运算(n2 op n1),将获得的结果数入栈
重复 1 和 2 直到表达式扫描结束,那么栈中最后剩余的数便是表达式的值。
比如上面的例子,1 + (3 - 2) * 4 / 5 = 1.8,对于后缀表达式 1 3 2 - 4 * 5 / +:
当前 Token
操作
栈(栈顶在左边)
1
遇到数直接入栈
1
2
遇到数直接入栈
3 1
3
遇到数直接入栈
2 3 1
-
n1 = 2, n2 = 3;n2 op n1 = 3 – 2 = 1,并将 1 入栈
1 1
4
遇到数直接入栈
4 1 1
*
n1 = 4, n2 = 1;n2 op n1 = 4 * 1 = 4,并将 4 入栈
4 1
5
遇到数直接入栈
5 4 1
/
n1 = 5, n2 = 4;n2 op n1 = 4 / 5 = 0.8,并将 0.8 入栈
0.8 1
+
n1 = 0.8, n2 = 1;n2 op n1 = 1 + 0.8 = 1.8,并将 1.8 入栈
1.8
所以可见计算后缀表达式非常容易编码。
由 上一篇 文章可知,我们目前的 Expression 类所表示的,就是中缀表达式,所以我们需要提供算法,将中缀表达式转换为前缀表达式或者后缀表达式,从而方便我们计算表达式的值。当然,算法的流程,我们的计算机先辈们早就想出来了,而我们只需要做出实现即可。
同样以后缀表达式为例,中缀表达式转换为后缀表达式的算法流程如下:
初始化运算符栈 S 和用来保存中间结果的列表 L
从左往右扫描中缀表达式:
遇到数时,直接将其加入到 L
遇到运算符 op 时
2.1 如果 S 为空,那么直接将 op 入栈 S
2.2 如果 S 不空,并且 S 栈顶为左括号 '(',那么将 op 入栈 S
2.3 如果 S 不空,此时 S 栈顶为运算符ÿ