学习来自:https://www.cnblogs.com/zingp/p/8666214.html 感谢!
思路:
- 考虑的四则运算符号 : ["+", "-", "*", "/", "(", ")"]
- 获取表达式字符串 --》表达式切分 (请注意区分 减号"-", 与负号"-" ) --》
- 先出初切:
formula_list = [i for i in re.split('(\-\d\.?\d*)', formula) if i]
然后再判定:if re.search('[\+\-\*\/\(]$', final_formula[-1]):
- 其他正常切分就行:
item_split = [i for i in re.split('([\+\-\*\/\(\)])', item) if i]
- 先出初切:
- 得到合适的 字符子串 --》 区分 属于 数字栈 还是 操作符栈 -》存好后
- 运算逻辑
- 遍历表达式split后结果,如果数字来了需要压栈,进入 num_stack,
- 如果运算符来了,判定:
- 第一个运算符 直接压栈
- 新的后面的运算符来了,先拿出之前的运算符栈中最后的运算符弹出,比较优先级
- 如果优先级相同,数字栈弹出后两个元素与老运算符操作 -> 得到的结果压入数字栈
- 如果优先级不同,新的运算符压栈
- 注意,如果老运算符弹出为“(”, 新运算符目前为“)”,-> 弹出老的"(", 丢弃新的“)”
- 所有item结束后,数字栈和运算符栈中可能还有元素,再搞一波,最后结果在 num_stack中
代码:
import re
def calculate(n1, n2, operator):
if operator == "+":
result = n1 + n2
elif operator == "-":
result = n1 - n2
elif operator == "*":
result = n1 * n2
elif operator == "/":
result = n1 / n2
else:
raise Exception("operator is not support now...")
return result
def is_operator(e):
operators = ["+", "-", "*", "/", "(", ")"]
return True if e in operators else False
def formula_format(formula):
"""
步骤需要处理的是区分横杠‘-’是代表负数还是减号
"""
formula = re.sub(' ', '', formula)
# 以 '横杠数字' 分割, -> \- 表示匹配横杠开头; \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
def decision(tail_op, now_op):
"""
:param tail_op: 运算符栈最后一个运算符
:param now_op: 算式列表取出当前运算符
:return: 1 弹栈, 0 弹出运算符栈最后一个元素, -1 入栈
"""
# 运算符等级
rate1 = ["+", "-"]
rate2 = ["*", "/"]
rate3 = ["("]
rate4 = [")"]
if tail_op in rate1:
if now_op in rate2 or now_op in rate3:
# 运算符优先级不同
return -1 # 把当前取出的运算符压栈 "1+2+3"
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
# 主函数 -> 遍历算式列表中的字符,决定入栈弹栈操作
def final_cal(formula_list):
num_stack = []
op_stack = []
for e in formula_list:
operator = is_operator(e)
if not operator:
# 数字栈 -> 转为浮点数
a = 2
num_stack.append(float(e))
else:
# e 开始进入 运算符栈, -> 判断
while True:
a = 1
if len(op_stack) == 0: #第一个运算符来了,都得入栈
op_stack.append(e)
break
# 后面运算符来了,需要判断入栈,or 出栈。
pop_oper = op_stack[-1]
tag = decision(op_stack[-1], e)
if tag == -1: # 压栈
op_stack.append(e)
break
elif tag == 0: #弹出运算符栈内最后一个 "(", 丢掉当前的 ")", 进入下次循环
op_stack.pop()
break
elif tag == 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
if __name__ == "__main__":
# formula = "1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2))"
formula = "1 - (2 + (-6 + (-2))) "
formula_list = formula_format(formula)
result, _ = final_cal(formula_list)
print('result = ', result)
结果:
result = [7.0]