递归法求解算术表达式

问题引出

逆波兰式的递归定义如下

如果算术表达式E = (E'),则RPN(E) = E'
如果算术表达式E = E1 OPERAND E2,则RPN(E) = RPN(E1) RPN(E2) OPERAND
注:RPN 表示 Reverse Polish notation

其中问题求解的难点在于,对于第二条规则如何分割子串。编译原理课上老师回避了这个问题,而使用了常见的算符栈的方法求解,但仔细思考,可以通过程序方法找到分割位置。

解决方案

通过初步观察可知,分割点算符常常比左右相邻算符优先级低,我们可以利用算符栈算法中的算符优先矩阵得到一条表达式的优先关系,但为了编程方便,我们在表达式两端加入‘#’,并规定‘#’的优先级无限大。例:

表达式a + b * c在两端加上‘#’得到# a + b * c #,计算优先级可得# > + < * < #。所以‘+’为优先级的最低点,从‘+’处分割子串。

进一步考虑括号的问题,我们得出结论,由于括号的优先级永远是最高的,分割点一定不再括号之中。于是我们可以巧妙的设置一个标志变量bracket,在扫描到左括号时bracket -= 1,在扫描到右括号时,bracket += 1。只有在bracket == 0时才判断是否为分割点。若找不到分割点则说明括号不匹配,表达式语法出错。

另外考虑逆波兰式定义的第一条,在括号在表达式两端匹配时,应该去除括号。

由此,我们得到了递归法求解算术表达式的完整算法:

(1) 如果len(E) == 1则返回int(E)
(2) 如果算术表达式形如E = (E')E = E'
(3) 初始化:OP = i = bracket = 0, 表达式两端加‘#’
(4) 循环:
       (4.1) 如果E[i] == '('bracket -= 1
       (4.2) 否则E[i] == ')'bracket += 1
       (4.3) 如果bracket == 0E[i]的优先级小于E[OP]OP = i
       (4.4) i += 1
(5) 返回RPN(E[0:OP]) RPN(E[OP + 1:]) E[OP]

Python实现

注意本代码求值,而上述伪代码求逆波兰式,两者在一些细节上略有不同

def trim_bracket(exp):
	while (exp[0], exp[-1]) == ('(', ')'):
		exp = exp[1: -1]
	return exp


def calculate(exp, prior_matrix):
	if len(exp) == 1:
		return int(exp)
	exp = "#" + exp + "#"
	op = i = bracket = 0
	while i < len(exp):
		bracket += {'(': -1, ')': 1}.get(exp[i], 0)
		if bracket == 0 and prior_matrix[exp[op]].get(exp[i], -1) > 0:
			op = i
		i += 1
	val_1 = calculate(trim_bracket(exp[1: op]), prior_matrix)
	val_2 = calculate(trim_bracket(exp[op + 1: -1]), prior_matrix)
	return val_1 + val_2 if exp[op] == '+' else val_1 - val_2 if exp[op] == '-' else \
		val_1 * val_2 if exp[op] == '*' else val_1 / val_2


def calculate_expression(exp):
	prior_matrix = {
		'+': {'+': 1, '-': 1, '*':-1, '/':-1, '#':-1},
		'-': {'+': 1, '-': 1, '*':-1, '/':-1, '#':-1},
		'*': {'+': 1, '-': 1, '*': 1, '/': 1, '#':-1},
		'/': {'+': 1, '-': 1, '*': 1, '/': 1, '#':-1},
		'#': {'+': 1, '-': 1, '*': 1, '/': 1, '#': 0},
	}
	print(exp[1: -1], "=", calculate(exp.replace(' ', ''), prior_matrix))
if __name__ == '__main__':
	calculate_expression("1 + 2 + 5 * (2 - 8 * 3) - (2 - 1)")
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值