Python 借助逆波兰表达式(后缀表达式)实现简单计算器
0. 参考资料
1. 中缀表达式转后缀表达式
中缀表达式转后缀表达式的过程中需要两个主要存储结构。
- 用于存放最终后缀表达式的列表
postfix
- 用于存放运算符的栈
operators
,可以使用Python
中的列表实现
转换步骤:
- 从左到右顺序扫描中缀表达式
- 如果扫描到操作数,直接追加到后缀表达式
postfix
中 - 如果扫描到运算符:
(
左括号: 直接压栈到运算符栈operators
)
右括号: 依次弹出运算符栈中的元素,并放入到后缀表达式postfix
中; 直到遇到(
左括号,把它弹出后丢弃,后缀表达式中不需要括号。- 加减乘除:
如果该运算符优先级高于栈顶元素,则将它压栈到
operators
; 如果该运算符优先级低于栈顶元素,依次弹出栈顶元素并把它们存入到后缀表达式postfix
中,直到遇到(
左括号或者优先级低于该运算符的栈顶元素时,保持栈顶元素不变,并将该运算符压栈到operators
。
- 扫描完毕中缀表达式后,将运算符栈
operators
中的其余元素依次弹出并追加到后缀表达式列表中。
2. 后缀表达式的求值
后缀表达式的求值过程中需要一个操作数栈 operands
来存储中间结果,可以用 Python
的列表来实现。
求值步骤如下:
- 从左到右扫描后缀表达式
- 如果是操作数,将操作数压栈
- 如果是运算符,弹出两个操作数进行运算(注意顺序,先弹出是右操作数,后弹出的是左操作数)
- 将运算结果压栈
- 将后缀表达式扫描完毕
- 最后操作数栈
operands
就剩一个元素,该元素为后缀表达式的结果
3. Python 代码实现
# 运算符元组
OPERATORS = ('+', '-', '*', '/', '(', ')')
# 优先级字典
PRIORITY = dict([
('+', 1),
('-', 1),
('*', 2),
('/', 2)
])
def pop_left_bracket(postfix, operators):
"""依次弹栈并追加到后缀表达式,直到遇到左括号为止。"""
while operators:
operator = operators.pop()
if operator == '(':
break
else:
postfix.append(operator)
def compare_and_pop(i, postfix, operators):
"""比较优先级并进行相应操作。"""
if len(operators) == 0:
operators.append(i)
return
while operators:
operator = operators.pop()
if operator == '(':
operators += ['(', i]
return
elif PRIORITY[i] > PRIORITY[operator]:
operators += [operator, i]
return
else:
postfix.append(operator)
operators.append(i)
def pop_rest(postfix, operators):
"""弹出所有剩余的运算符,追加到后缀表达式。"""
while operators:
postfix.append(operators.pop())
def valid_infix(infix):
pass
def is_number(text):
"""判断字符串是否为整数或者浮点数。"""
try:
return int(text)
except:
try:
return float(text)
except:
return None
def infix_to_postfix(infix):
"""将中缀表达式转换为后缀表达式。"""
infix = infix.split()
postfix = []
operators = []
# TODO jpch89: 检查中缀表达式合法性
# if not valid_infix(infix):
# return None
for i in infix:
if is_number(i):
postfix.append(i)
elif i in OPERATORS:
if i == '(':
operators.append(i)
elif i == ')':
pop_left_bracket(postfix, operators)
else:
compare_and_pop(i, postfix, operators)
pop_rest(postfix, operators)
return postfix
def calc_postfix(postfix):
"""计算后缀表达式的值。"""
operands = []
for i in postfix:
if is_number(i):
operands.append(i)
else:
right = operands.pop()
left = operands.pop()
operands.append(str(eval(left + i + right)))
try:
return int(operands[0])
except ValueError:
return float(operands[0])
def main():
infix = input('输入算式(请用空格分隔数字/运算符/括号):')
postfix = infix_to_postfix(infix)
if postfix is not None:
print('后缀表达式为:%s' % ' '.join(postfix))
else:
print('不合法的算式!')
exit()
result = calc_postfix(postfix)
print('计算结果为:%s' % result)
if __name__ == '__main__':
main()
4. 待完善
- 中缀表达式的合法性检测
- 目前的代码强制使用空格分割运算符和操作数,需要去掉这个限制
完成于 2018.12.04