【讲古堂】表达式求值
(dubenju@126.com 2015/12/27)
什么是表达式
表达式是由数字,操作符,变量,常量等有意义地组合而成并能求得结果的式子。
例如:
32 + ( ( 9 * Celsius ) / 5 )
4 + 2 * 55 / 2.5
组成表达式的信息种类繁多,这里只讨论数字表达式,即表达式由以下要素构成:
数字、操作符、变量、常量。
数字
不考虑进制的话,通常指十进制数,小数的时候是有小数点的。数字通常是以被操作对象的身分出现在表达式中的,叫做操作数。
操作符
表示对操作数进行哪种操作的符号叫做操作符,被操作的值叫做操作数,对操作数进行操作的过程称为表达式求值。根据操作对象的个数分为一元操作符和二元操作符。
一元操作符,操作只应用于单一操作数。例如:23!-n。
操作应用于两个操作数的叫二元操作符。例如:6*21、29/11、6.9+19.8、21.1-2.8
优先级
相对其他操作符,每个操作符都有一个优先级,优先级高的操作符比优先级低的操作符优先应用。一般的优先级是这样设置的:
分组操作符()具有最高优先级。
一元操作符-+比乘除模的优先级高。
次方乘除模比加减法的高。
操作数的类型变换
类型变换是指操作的过程中操作数的类型发生变化的现象。
比如:
1/3
1.5/0.3
变量与常量
变量是指值可以变化的量。比如x+5中的x。
常量是指一般不发生变化并代表某一数值的量。比如:pi=3.1415926。
表达式的递归
这里所谓的递归是指一个表达式的结果可以作为另一表达式的操作数。
例如:
1 + 2 * 3
2 * 3
1 + 6
表达式的表示方法
中缀表示
1 + x * 3 + 4 / x
操作符是以中缀形式处于操作数的中间(例:3 + 4)。中缀表达式不容易被计算机解析,但因为它符合人们的普遍用法,被许多程序语言使用。相当于语法树的中序遍历得到的结果。
前缀表示(波兰式)
+ + 1 * x 3 / 4 x
操作符置于操作数的前面。如果操作符的元数(arity)是固定的,则语法上不需要括号仍然能被无歧义地解析。波兰式是波兰数学家扬·武卡谢维奇1920年代引入的,用于简化命题逻辑。相当于语法树的前序遍历得到的结果。
后缀表示(逆波兰式)
1 x 3 * + 4 x / +
操作符置于操作数的后面。逆波兰记法不需要括号来标识操作符的优先级。易于程序实现。相当于语法树的后序遍历得到的结果。
表达式的分解parse
把表达式分解成不可再细分的基本元素的过程。
记号(token)的类型
变量variable
数值number
操作符operator
字符0-9和小数点.的话则是数值型。
是预先登记的变量的话则是变量型(常量等同于变量)。
若是操作符的话则是操作符型。
上述以外的话则是不正确的类型。
表达式的分析
语法检查
语法错误就是表达式没有遵循分析程序的严格规则。多数情况下,语法错误都是人为错误,比如输入失误等,以下表达式是非法的10**8(10-5)*9)/8
表达式求值
基于中缀表示的表达式求值
分别针对操作数和操作符构建表达式,必要时采用递归的方式。由于实现起来比较复杂,多不采用,故不作细述。
基于前缀表示的表达式求值
对于解析后的记号,从后向前进行,如果扫描到操作数,则压进栈,如果扫描到操作符,则根据操作符的操作数个数从栈中弹出相应个数的操作数来进行相应的操作,并将结果压进栈,当扫描结束后,栈的栈顶就是表达式结果。
基于逆波兰表示的表达式求值
针对逆波兰,可以用一个栈来实现计算,扫描从左往右进行,如果扫描到操作数,则压进栈,如果扫描到操作符,则根据操作符的操作数个数从栈中弹出相应个数的操作数来进行相应的操作,并将结果压进栈,当扫描结束后,栈的栈顶就是表达式结果。
调度场算法
中缀表达式转换成后缀表达式
既然中缀表达式对于计算机的运算并不便利,而前缀后缀表达式的计算相对简单方便。因此,找到一种途径将中缀表达式转换成前缀后缀表达式就十分重要。实际上,二者的转换算法看起来也很像一个逆过程。因此,我们着重讨论中缀转后缀。
从理论上讲,已知一棵二叉树的中序遍历序列,要求出它的后序遍历序列是不唯一的,即文法是有多义性的。但是,在这里加上了优先级这一限制条件,转换就变得唯一了。
所谓的调度场算法是操作数直接输出,操作符的话要和栈内的操作符进行比较,优先级高的出栈输出,然后当前操作符入栈。在最后,把栈内剩余的全部输出。这里的栈起到了调度场的作用。
中缀表达式转换成前缀表达式
中缀表达式转换成前缀表达式和中缀表达式转换成后缀表达式十分类似,只需要将扫描方向由前往后变成由后往前,将'('改为')',')'改为'(',注意其中一个判断优先级的地方需要由>=变成>即可。
来源:oschina
链接:https://my.oschina.net/u/660460/blog/551970