使用Python实现输入表达式计算,并返回计算结果,主要思路如下:首先逐字符解析计算式,包括计算符号、计算数(注意对负数前负号‘-’的处理)等,然后根据先乘除后加减的计算优先级规则,对解析出来的计算单元执行计算。这里要特别注意对括号的处理,因为括号具有最高的优先级,因此处理算数表达式时,应该先搜索整个表达式的最后一个左括号,再搜索与之匹配的右括号,截取两个括号之间的表达式,先进行运算,计算完括号内表达式之后,用计算结果代替左右括号内所有的内容,并且删除左右括号,计算结果作为一个新的操作数进行处理。如此循环直到所有操作完成。
为了实现上述的计算过程,这里我推荐创建两个堆栈,数字栈和运算符栈,并用一个指针从前往后扫描整个表达式。数字栈保存扫描表达式时提取的操作数以及括号内表达式的中间结果,运算符栈则保存表达式的运算符(+,-,*,/)。
下面介绍具体操作过程:
- 指针指向数字,直接将数字压入数字栈,指针下移。
- 指针指向运算符,当栈顶的运算符为左括号,且当前的运算符是右括号,则直接将左括号出栈,指针下移。这种情况不需要计算,直接出栈就行,因为括号内计算已经完成了。
- 指针指向运算符,但当前栈顶为左括号或者是空栈,又或者当前运算符的优先级高于或者等于栈顶运算符时,应该将当前运算符入栈,指针下移。
- 除以上情况外的任何情况,从数字栈弹出两个数字作为操作数,从运算符栈弹出一个运算符,参与运输并把获得的运算结果压入数字栈,指针不下移。
重复以上操作过程,直至运算符栈为空,返回数字栈顶元素,也就是表达式的计算结果。但如果出现了以下情况的任何一种情况,就说明原本的表达式不合规范,表达式本身有错误:
- 运算符栈为空时,数字栈内元素个数大于1
- 数字栈空了,或只存在一个元素时,运算符栈未空
上代码
步骤1:定义两数字之间的操作计算
def calculate(n1, n2, operator):
'''
:param n1: float
:param n2: float
:param operator: + - * /
:return: float
'''
result = 0
if operator == "+":
result = n1 + n2
if operator == "-":
result = n1 - n2
if operator == "*":
result = n1 * n2
if operator == "/":
result = n1 / n2
return result
# 判断是否是运算符,如果是返回True
def is_operator(e):
'''
:param e: str
:return: bool
'''
opers = ['+', '-', '*', '/', '(', ')']
return True if e in opers else False
步骤2:将字符串表达式分割成操作数和运算符,并按顺序保存在表达式列表内
# 能够按照所能匹配的字串将字符串进行切分,返回切分后的字符串列表
re.split(pattern, string[, maxsplit=0, flags=0])
import re
# 将算式处理成列表,解决-是负数还是减号
def formula_format(formula):
# 去掉算式中的空格
formula = re.sub(' ', '', formula)
# 以 '横杠数字' 分割, 其中正则表达式:(\-\d+\.?\d*) 括号内:
# \- 表示匹配横杠开头;
# \d+ 表示匹配数字1次或多次;
# \.?表示匹配小数点0次或1次;
# \d*表示匹配数字1次或多次。
formula_list = [i for i in re.split('(\-\d+\.?\d*)', formula) if i]
# 最终的算式列表
final_formula = []
for item in formula_list:
# 第一个是以横杠开头的数字(包括小数)final_formula。
# 即第一个是负数,横杠就不是减号
if len(final_formula) == 0 and re.search('^\-\d+\.?\d*$', item):
final_formula.append(item)
continue
if len(final_formula) > 0:
# 如果final_formal最后一个元素是运算符['+', '-', '*', '/', '(']
# 则横杠数字不是负数
if re.search('[\+\-\*\/\(]$', final_formula[-1]):
final_formula.append(item)
continue
# 按照运算符分割开
item_split = [i for i in re.split('([\+\-\*\/\(\)])', item) if i]
final_formula += item_split
return final_formula
步骤3:定义运算符压栈的决策函数
def decision(tail_op, now_op):
'''
:param tail_op: 运算符栈的最后一个运算符
:param now_op: 从算式列表取出的当前运算符
:return: 1 代表弹栈运算,0 代表弹运算符栈最后一个元素, -1 表示入栈
'''
# 定义4种运算符级别
rate1 = ['+', '-']
rate2 = ['*', '/']
rate3 = ['(']
rate4 = [')']
if tail_op in rate1:
if now_op in rate2 or now_op in rate3:
# 说明连续两个运算优先级不一样,需要入栈
return -1
else:
return 1
elif tail_op in rate2:
if now_op in rate3:
return -1
else:
return 1
elif tail_op in rate3:
if now_op in rate4:
return 0 # ( 遇上 ) 需要弹出 (,丢掉 )
else:
return -1 # 只要栈顶元素为(,当前元素不是)都应入栈。
else: # 栈顶运算符和当前运算符优先级相同
return -1
步骤4:实现表达式计算
def final_calc(formula_list):
num_stack = [] # 数字栈
op_stack = [] # 运算符栈
for e in formula_list:
operator = is_operator(e)
if not operator:
# 压入数字栈
# 字符串转换为符点数
num_stack.append(float(e))
else:
# 如果是运算符
while True:
# 如果运算符栈等于0无条件入栈
if len(op_stack) == 0:
op_stack.append(e)
break
# decision 函数做决策
tag = decision(op_stack[-1], e)
if tag == -1:
# 如果是-1压入运算符栈进入下一次循环
op_stack.append(e)
break
elif tag == 0:
# 如果是0弹出运算符栈内最后一个(, 丢掉当前),进入下一次循环
op_stack.pop()
break
elif tag == 1:
# 如果是1弹出运算符栈内最后两个元素,弹出数字栈最后两位元素。
op = op_stack.pop()
num2 = num_stack.pop()
num1 = num_stack.pop()
# 执行计算
# 计算之后压入数字栈
num_stack.append(calculate(num1, num2, op))
# 处理大循环结束后 数字栈和运算符栈中可能还有元素 的情况
while len(op_stack) != 0:
op = op_stack.pop()
num2 = num_stack.pop()
num1 = num_stack.pop()
num_stack.append(calculate(num1, num2, op))
return num_stack, op_stack
步骤5:编写主程序,计算表达式结果
if __name__ == '__main__':
formula = input('请输入:\n')
formula_list = formula_format(formula)
result, _ = final_calc(formula_list)
print("计算结果:\n", result[0])