我们知道在计算的时候,运算符的优先级是很关键的,如乘除法的优先级要高于加减法,而括号里面的优先级要高于括号外面的优先级。为了表示运算符的优先级,我们先定一个哈希表来表示运算符和其优先级
# value越大 优先级越高
symbolDict = {"+":0,"-":0,"*":1,"/":1,"^":2,"(":3,")":3}
如果我们要实现一个简单的计算器,我们就需要正确的处理运算符的优先级。其基本思想就是先完成优先级高的运算,再完成优先级较低的运算。由于优先级较低的运算可能出现在优先级高的运算之前,所以我们用一个栈来保存运算符,当它的优先级大于等于下一个运算符时,将其离栈进行计算。下面介绍中缀表达式和后缀表达式两种表达式。
中缀表达式
中缀表达式就是我们生活中最常用的表达式,运算符在两个操作数的中间,如result=a+b。对于一个复合的中缀表达式result=a+b*c,通常分步先计算d=b*c,再计算result=a+b。
后缀表达式
顾名思义,后缀表达式表示运算符在两个操作数的后面,如:ab+对应的中缀表达式为a+b。对于复合的后缀表达式abc*+来说,对应的中缀表达式为a+b*c。后缀表达式的优点在于不用考虑运算符的优先级,直接根据表达式就可以从左到右计算出结果。
后缀表达式的求解代码为:
def cal(back):
'''
back 是一个保存后缀表达式的list
如:[1,2,"+",3,"*"] 对应12+3* 即(1+2)*3
'''
# 操作数的栈
numsStack = []
for i in back:
# 遍历到操作数时 将操作数push进栈
if i not in symbolDict.keys():
numsStack.append(i)
# 遍历到运算符时 进行计算 并将计算结果push进栈
else:
# 从栈中取出对应两个操作数
a,b = numsStack.pop(),numsStack.pop()
# 进行计算 并将结果push进栈
# 需要注意的是在这里b是第一个操作数而a是第二个操作数
numsStack.append(baseCal(a,b,i))
# 返回结果
return numsStack.pop()
运行结果如下:
In [2]: back
Out[2]: [1.0, 5.0, 10.0, 8.0, 4.0, 2.0, '-', '/', '-', '*', '+']
对应的:1+5*(10-8/(4-2))
In [3]: result = cal(back)
In [4]: result
Out[4]: 31.0
In [5]: 1+5*(10-8/(4-2))
Out[5]: 31
所以将输入的中缀表达式转换成后缀表达式,就可以从左到右计算出结果了。通过比较python自带的计算器可以看出,我们的程序的结果是正确的。
中缀表达式转化成后缀表达式
中缀表达式转后缀表达式最主要的就是确定表达式的计算过程。后缀表达式和中缀表达式中的操作数的顺序是不变的,而运算符则根据优先级进行重新排列,所以当我们在进行转化的过程中,对于操作数就直接输出。而对于运算符的操作,我们也需要一个栈来进行保存(因为运算符在两个操作后面,所以每个运算符都需要进栈进行保存),而栈顶元素出栈的判断条件就是它的优先级是否比下一个运算符高或与之相等(判断表达式运算执行的先后顺序)。对于括号的操作,遇到”(“时将其push进栈,遇到”)”时,对栈进行输出,直到输出为”(“时停止(注:”(“和”)”不输出)。
具体程序如下:
def midToBack(equation):
'''
str equation 输入的中缀表达式的字符串
'''
# 运算符栈
symbolStack = []
# 操作数
nums = ""
# 返回的后缀表达式(list)
retval = []
# 从左到右便利equation
i = 0
while i<len(equation):
# 判断当前是否为运算符 若不是 继续读取操作数
if not symbolDict.has_key(equation[i]):
nums+=equation[i]
else:
# 判断是否读取了操作数,主要是带括号的表达式会连续出现多个运算符
if nums:
# 直接输出操作数
retval.append(float(nums))
nums = ""
# 处理当前的运算符
# 如果栈为空,将运算符push进栈
if not symbolStack:
symbolStack.append(equation[i])
else:
# 处理")"的情况
if equation[i] == ")":
while symbolStack and symbolStack[-1]!="(":
retval.append(symbolStack.pop())
symbolStack.pop()
# 判断哪些栈中的运算符可以出栈
else:
while symbolStack and symbolDict[equation[i]]<=symbolDict[symbolStack[-1]]:
if symbolStack[-1] == "(":
break
retval.append(symbolStack.pop())
symbolStack.append(equation[i])
i += 1
# 将剩余的操作数和运算符输出
if nums:
retval.append(float(nums))
while symbolStack:
retval.append(symbolStack.pop())
return retval
运行结果如下:
In [1]: back = midToBack("1+5*(10-8/(4-2))")
1+5*(10-8/(4-2))
In [2]: back
Out[2]: [1.0, 5.0, 10.0, 8.0, 4.0, 2.0, '-', '/', '-', '*', '+']