Python计算器(模拟eval)
目的:实现同Python中eval函数一样,对于复杂算术运算提供一个简易的计算器;
思路:
1、通过正则表达式,将运算式子中所有的数字和运算符分割开来形成类表的方式,然后可以方便列表检索进行运算,eq_format(eq)函数;
2、通过列表的检索,通过递归的运算,一层层的剥离掉(),然后进行+,-,*,/,最终得到完全没有括号的算式,进行最后一步的运算,期间需要处理+-,--的情况出现,主要涉及到change(eq,count)函数;
(对于()剥离的情况,主要是通过遍历列表,如果遇到左括号,则把当前左括号的索引赋值给参数bracket,直到遇到第一个右括号,此时的索引与bracket中间的元素即为最内层括号的元素,用切片的方式提取出来,通过 calculate() 函数计算出值,然后用计算结果去替换掉此时左括号到第一个右括号的元素,此时去除第一层括号,然后进入递归,不断递归直至去除所有括号;
可能遇到的问题:
不要用index的方式去取当前左括号的索引,因为列表的index方法返回的一直都是第一个左括号的索引,而不是当前左括号的索引,会导致出错。因此在函数内用参数 自定义的参数count 进行计数当前索引值。用计算得出的值来替换掉第一层括号部分后,有可能会出现 ‘+-’ ,‘ - -’的情况,然后用change() 函数进行处理)
3、去括号的方式,主要是从最内层开始突破,通过运算得到结果替换掉(),然后一层层向外运算剥离,最后得到结果,主要是simplify(format_list)函数中嵌套change()函数和calculate()函数进行运算取括号以及特殊处理;
4、对于乘除的运算详见deal_multiplication_division()函数部分,方法也是递归,处理掉一层乘除以后得到的结果进行判断处理掉+-,--的情况,然后进入下一步运算,其中嵌套change(eq,count)函数来进行特殊处理;
5、对于加减的运算详见deal_plus_minus()函数部分,方法同样是递归;
6、整体运算的过程主要是calculate(s_eq)函数,通过判断是加减还是乘除的方式嵌套deal_multiplication_division()函数和deal_plus_minus()函数,然后嵌套在simplify()函数当中进行运算;
7、最后使用calculator(eq)主函数,嵌套simplify()和calculate()函数进行最后的计算得到结果;
详细代码如下:
import re def eq_format(eq): #格式化计算式函数 ''' :param eq:输入的算术字符串 :return: 格式化后的列表,例如['-','2','+','3','*','12','/','123'] ''' format_list = re.findall('[\d\.]+|\(|\+|\-|\*|\/|\)',eq) #print(format_list) '[\d\.]+|\(|\+|\-|\*|\/|\)' return format_list #返回格式化的列表形式内容 def change(eq,count): #解决同时出现+-,--类的格式化列表 ''' :param eq:去掉括号以后或者乘除后的格式化列表 :param count: 发生变化的元素的索引位置 :return: 返回一个没有+-,--等类的格式化列表内容 ''' if eq[count] == '-': #如果第count元素是-的话 if eq[count-1] == '-': #且第count-1元素也是- eq[count-1] = '+' #那么调整count-1元素为+ del eq[count] #删除第count元素内容 elif eq[count-1] == '+': eq[count-1] = '-' del eq[count] return eq def deal_multiplication_division(eq): #处理所有的乘除的计算方程式 ''' :param eq:处理带有乘除符号的格式化列表 :return: 去掉乘除好以后的格式化列表 ''' count = 0 for i in eq: if i == '*': if eq[count+1] != '-': eq[count-1] = float(eq[count-1]) * float(eq[count+1]) del(eq[count]) del(eq[count]) elif eq[count+1] == '-': eq[count] = float(eq[count-1]) * float(eq[count+2]) eq[count - 1] = '-' del(eq[count+1]) del(eq[count+1]) eq = change(eq,count-1) return deal_multiplication_division(eq) elif i == '/': if eq[count+1] != '-': eq[count-1] = float(eq[count-1]) / float(eq[count+1]) del(eq[count]) del(eq[count]) elif eq[count+1] == '-': eq[count] = float(eq[count-1]) / float(eq[count+2]) eq[count-1] = '-' del(eq[count+1]) del(eq[count+1]) eq = change(eq,count-1) return deal_multiplication_division(eq) count = count + 1 return eq def deal_plus_minus(eq): #处理所有的加减方程式的计算 ''' :param eq:只带有加减号的格式化列表 :return: 计算出整个列表的结果 ''' count = 0 if eq[0] != '-': sum = float(eq[0]) else: sum = 0.0 for i in eq: if i == '-': sum = sum - float(eq[count+1]) elif i == '+': sum = sum + float(eq[count+1]) count = count + 1 if sum >= 0: eq = [str(sum)] else: eq = ['-',str(-sum)] return eq def calculate(s_eq): #处理进行加减乘除 ''' :param s_eq:不带括号的格式化列表 :return: 返回最终的计算结果 ''' if '*' or '/' in s_eq: s_eq = deal_multiplication_division(s_eq) if '+' or '-' in s_eq: s_eq = deal_plus_minus(s_eq) return s_eq def simplify(format_list): #递归方式去掉括号,并计算到最后一层没有括号 ''' :param format_list:输入的算是格式化列表如['-','2','+','3','*','12','/','123'] :return:通过递归去掉括号,返回最终简化后没有括号的列表 ''' bracket = 0 #用于存放左括号在格式化列表中的索引 count = 0 for i in format_list: if i == '(': bracket = count elif i == ')': temp = format_list[bracket + 1 : count] new_temp = calculate(temp) format_list = format_list[:bracket] + new_temp + format_list[count+1:] format_list = change(format_list,bracket) #解决去括号后出现的'--','+-'问题 return simplify(format_list) #递归去掉括号 count = count + 1 return format_list #当递归到最后一层层的时候,不在有括号,因此返回最后无括号的列表内容 def calculator(eq): #计算主函数,主要判断最终计算结果的正负值,并返回 format_list = eq_format(eq) # 把字符串变成格式化的列表形式 s_eq = simplify(format_list) #去掉括号,得到一个无括号的格式化列表 ans = calculate(s_eq) #计算最终结果 if len(ans) == 2: #判断最终结果的正负值并返回结果 ans = -float(ans[1]) else: ans = float(ans[0]) return ans #返回最终的结果 if __name__ == '__main__': equation = '1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))' ans = calculator(equation) print('eval运算结果:',eval(equation)) print('程序运算结果:',ans)
参考链接:
link
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
【踩坑经验】Spring Webflux 的优雅关闭(Graceful Shutdown)
背景 最近在吃 webflux 这只螃蟹,发现虽然文档中写的会优雅关闭,但其实并没有等待所有请求返回再 shutdown. 如果有还未完成的请求(如sleep 10s的请求),会直接 Empty reply (环境: spring boot 2.1.5 with reactor-netty 0.8.8) 根因&解法 跳过分析不表,直接说结论 虽然 netty 本身有 graceful shutdown,并且在关闭时也的确调用到了,但是 reactor netty 调用的方式如下 //reactor.netty.resources.LoopResources#dispose @Override default void dispose() { //noop default disposeLater().subscrib
- 下一篇
vscode插件快餐教程(8) - LSP文本同步
vscode插件快餐教程(8) - LSP文本同步 这一节开始我们介绍下通过LSP进行文本同步的方法。 文件打开 我们先从简单的做起,先监听文件的打开。我们看一下LSP协议中对此部分的支持,参数是DidChangeTextDocumentParams结构。 微软的SDK在LSP的基础上是做了封装的,我们看下封装后的接口: 当前,TextDocument提供了4个属性: uri: 文件的URI version: 文件的版本号 languageId: 编程语言 lineCount: 有多少行另外还有3个函数: getText(): 获取文本 positionAt和offsetAt用于Position和offset的转换 我们来看个例子: documents.onDidOpen( (event: TextDocumentChangeEvent) =>
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Red5直播服务器,属于Java语言的直播服务器
- CentOS7,CentOS8安装Elasticsearch6.8.6
- CentOS8编译安装MySQL8.0.19
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS6,CentOS7官方镜像安装Oracle11G
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8安装Docker,最新的服务器搭配容器使用