当前位置: 首页 > news >正文

没有括号的字符串四则运算

目录

  • 问题
  • 分析与解答
  • eval
  • sympy
  • 自己编写
  • 逆波兰表达式
  • 拓展思考
  • 参考资料

问题

用代码实现一个method,这个method的入参是一个字符串,这个字符串是一个四则运算的算式,比如“1+2*3+4/2-3”;返回值是这个算式的运算结果,比如“1+2*3+4/2-3”的返回值是6,为了简化这个题目,这个入参的算式只包含加减乘除,不包含括号

分析与解答

四则运算是数学常见的运算法则,有现成的函数或者第三方库来解决,也可以自己写一个method。下面分别介绍几种方法,按照由易到难的顺序

eval

由于入参的算式只包含加减乘除,不包含括号,所以可以采用eval直接算,但是eval会存在注入风险。

# -*- encoding: utf-8 -*-
'''
@Project :   Arithemitc
@Desc    :   字符串转四则运算
@Time    :   2024/05/25 09:28:12
@Author  :   帅帅de三叔,zengbowengood@163.com
'''
import re
import operatordef arithmetic(expression):"""eval直接算"""return eval(expression)if __name__=="__main__":input = "1+2-3/2*2" #例子1+2-3/2*2, 1+2*3+4/2-3result = arithmetic(input)print(result)

sympy

sympy 是专用的数学符号计算库,在处理方程时候非常强大,但是运行效率稍微慢了点

import re
import operator
from sympy import sympify, simplifydef Arithemtic(origin_str): """去除优先级乘法和除法运算"""expr = str(origin_str) #转字符串result = simplify(sympify(expr))return resultif __name__=="__main__":input = "1+2-3/2*2" #1+2-3/2*2result = Arithemtic(input)print(result)

自己编写

倘若优先级运算“x”和“/”不相邻,此时,可以先对“x"和”/“先单独计算把“x"和”/“前后两个数字先计算和合二为一,处理完就只有”+“和”-“两种运算了,下面这个可以实现“x”和“/”不相邻的情形,“x”和“/”相邻的情况还没想好。

# -*- encoding: utf-8 -*-
'''
@Project :   Arithemitc
@Desc    :   字符串转四则运算
@Time    :   2024/05/25 09:28:12
@Author  :   帅帅de三叔,zengbowengood@163.com
'''
import re
import operatordef Arithemtic(origin_str): """去除优先级乘法和除法运算"""expr = str(origin_str) #转字符串digits = re.findall(r'\d+', expr) #提取数字串digits = [eval(i) for i in digits] #转数字operations = re.findall(r'[+\-*/]', expr) #提取运算符号print(digits, operations)operators = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv} #四则运算映射for i in range(len(operations)): #对运算列表循环try:if operations[i] == "*" or operations[i] =="/": #如果有乘法,除法,先算去除print(i, operations[i], digits[i], digits[i+1])digits[i] = operators[operations[i]](digits[i], digits[i+1]) #合并运算print(digits, operations)digits.pop(i+1) #剔除第i+1位数字operations.pop(i) #剔除第i位算符  except:continueprint(digits, operations)  #此时只有加减运算for j in range(len(operations)): # print(digits[j])     digits[j+1] =  operators[operations[j]](digits[j], digits[j + 1]) #咬合向前逐步运算result = digits[-1]print(result)return resultif __name__=="__main__":input = "1+2*3+4/2-3" #1+2-3/2*2result = Arithemtic(input)

逆波兰表达式

查阅资料说逆波兰表达式法可以用于解决没有括号的字符串四则运算,计算步骤如下:

1,字符串四则运算表达式转换为后缀表达式
2,从左到右遍历表达式中的每个元素;
3,如果当前元素是数字,将其压入栈中;
4,如果当前元素是运算符,则取出栈顶的两个数字进行计算,并将结果压入栈中;
5,当遍历完整个表达式后,栈顶就是最终结果。

import redef infix_to_postfix(expression):  # 运算符优先级字典,越低越优先  precedence = {'+': 1, '-': 1, '*': 2, '/': 2}  output = []  op_stack = []  # 辅助函数,用于检查是否为运算符  def is_operator(token):  return token in '+-*/'  # 分割表达式为tokens  tokens = re.findall(r'[+\-*/]|\d+', expression)    for token in tokens:  if token.isdigit():  # 如果是数字,直接输出  output.append(token)  elif is_operator(token):  # 如果是运算符  # 弹出并输出所有优先级更高或等于当前运算符的运算符  while op_stack and is_operator(op_stack[-1]) and \precedence[op_stack[-1]] >= precedence[token]:  output.append(op_stack.pop())  # 将当前运算符压入栈  op_stack.append(token)  else:  # 如果是非法字符,抛出异常  raise ValueError(f'Invalid token: {token}')  # 弹出并输出所有剩余的运算符  while op_stack:  output.append(op_stack.pop())  return ' '.join(output)  def evaluate_postfix(postfix_expr):  stack = []  for token in postfix_expr.split():  # 假设表达式是空格分隔的字符串  if token.isdigit():  # 如果是数字  stack.append(int(token))  else:  # 如果是运算符  operand2 = stack.pop()  operand1 = stack.pop()  if token == '+':  result = operand1 + operand2  elif token == '-':  result = operand1 - operand2  elif token == '*':  result = operand1 * operand2  elif token == '/':  # 注意:这里没有处理除数为0的情况  result = operand1 / operand2  else:  raise ValueError(f"Invalid operator: {token}")  stack.append(result)  return stack.pop()  # 返回最终结果  # 示例  
infix_expr = "1+2-3/2*2"   
postfix_expr = infix_to_postfix(infix_expr)  
result = evaluate_postfix(postfix_expr)
print(f"Postfix expression: {postfix_expr}")
print(f"result: {result}")

拓展思考

1,根据乘法与除法的关系,除以某个数等于乘以这个数的倒数,可以将所有除法运算改成乘法运算,这种改变只会改变运算符号的右侧不会动左侧的;

2,根据乘法是加法的简便运算将所有乘法改成加法运算,这里存在一个非整数倍的问题需要用数值解法;

3,最后化成只有加法和减法的表达式,因为是对字符串操作,其中需要用到大量的正则运算和定位索引。

参考资料

1,逆波兰表达式介绍及求值实现
https://blog.csdn.net/wenwenaier/article/details/121236053

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • android 水平居中对齐并举例
  • wordpress教程视频 wordpress教程网盘 wordpress教程推荐wordpress教程网
  • ERC314协议
  • 3.每日LeetCode-爬楼梯(Go,Java,Python)
  • SpringCloud系列(23)--手写实现负载轮询算法
  • 使用paddlepaddle框架构建ViT用于CIFAR10图像分类
  • 基于Vue2与3版本的Element UI与Element Plus入门
  • 【蓝桥杯选拔赛真题76】python找出元素 第十四届青少年组蓝桥杯python选拔赛真题 算法思维真题解析
  • LeetCode/NowCoder-链表经典算法OJ练习3
  • 使用elementUI的form表单校验时,错误提示位置异常解决方法
  • Qt学习记录(14)线程
  • pdf打开方式怎么设置默认?分享这几种设置方法
  • Ribbon负载均衡(自己总结的)
  • Wpf 使用 Prism 实战开发Day24
  • DA-CLIP论文阅读笔记
  • 2017前端实习生面试总结
  • Electron入门介绍
  • exports和module.exports
  • HTTP中GET与POST的区别 99%的错误认识
  • JS数组方法汇总
  • Just for fun——迅速写完快速排序
  • leetcode378. Kth Smallest Element in a Sorted Matrix
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • 翻译:Hystrix - How To Use
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 回顾2016
  • 实习面试笔记
  • 在Mac OS X上安装 Ruby运行环境
  • Linux权限管理(week1_day5)--技术流ken
  • MPAndroidChart 教程:Y轴 YAxis
  • ​flutter 代码混淆
  • ​你们这样子,耽误我的工作进度怎么办?
  • #Z2294. 打印树的直径
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • (~_~)
  • (02)Unity使用在线AI大模型(调用Python)
  • (160)时序收敛--->(10)时序收敛十
  • (k8s)Kubernetes本地存储接入
  • (PySpark)RDD实验实战——求商品销量排行
  • (二) Windows 下 Sublime Text 3 安装离线插件 Anaconda
  • (论文阅读30/100)Convolutional Pose Machines
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (七)Appdesigner-初步入门及常用组件的使用方法说明
  • (三)终结任务
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目
  • (一)u-boot-nand.bin的下载
  • (转)树状数组
  • (转载)在C#用WM_COPYDATA消息来实现两个进程之间传递数据
  • (轉貼) 蒼井そら挑戰筋肉擂台 (Misc)
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • .NET C# 使用 SetWindowsHookEx 监听鼠标或键盘消息以及此方法的坑
  • .net core 管理用户机密
  • .net core使用ef 6