0

0

实现一个计算器的实例

PHP中文网

PHP中文网

发布时间:2017-06-20 14:35:47

|

1633人浏览过

|

来源于php中文网

原创

作业:计算器开发

  (1)实现加减乘除及拓号优先级解析;

  (2)用户输入 1 - 2 * ( (60-30 +(-40/5) * (-9-2*5/-3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,必须自己解析里面的(),+,-,*,/符号和公式,运算后得出结果,结果必须与真实的计算器所得出的结果一致。

    代码如下:

 

 1 import re 2  3 formula = '1 - 2 * ( (60-30 +(-9-2*5/-3 + 7 /3*99/4*2998 +10 * 568/14 ) * (-40/5)) - (-4*3)/ (16-3*2) )' 4 #formula = "(1++1)" 5 def modify(formula_deep): 6     '''程序修饰函数,去除空格和括号''' 7     '''去除运算中出现的+- -- ++ -+ 等情形''' 8     formula_deep = re.sub("[() ]","",formula_deep)   #替换空格和空号 9     formula_deep = re.sub("\+-","-",formula_deep)    #替换+-为-10     formula_deep = re.sub("--",'+',formula_deep)     #替换--为+11     formula_deep = re.sub("-\+",'-',formula_deep)12     formula_deep = re.sub("\++","+",formula_deep)13     return formula_deep14 15 def multiply_divide(formula_deep):16     '''计算乘除'''17     '''由于乘除是首先计算的,我们的思路是,首先计算乘除,然后把计算的结果替换进去,就可以得到只剩加减的情形'''18     calc_sign = re.findall("[+-]",formula_deep)    #提取字符串中所有的加减号19     calc_list = re.split("[+-]",formula_deep)      #以加减号进行分割,得到乘除20     '''由于我们得到的calc_list:['', '9', '2*5/', '3', '7/3*99/4*2998', '10*568/14'],里面由于-号引起的麻烦,-9被分割了,2*5/等'''21     if calc_list[0] == '':22         '''处理列表开头“”空的情况,说明这里是负数,被我们分割掉了要重新进行合并'''23         calc_list[1] = calc_sign[0] + calc_list[1]24         del calc_sign[0]25         del calc_list[0]26     for num,line in enumerate(calc_list):27         '''处理2*5/的情形,说明这种后面除的是一个负数,因为只有负数才会出现这种情况2*5/-3被分割了,需要合并'''28         if line.endswith("/") or line.endswith("*"):29             '''如果结尾包括乘除号,说明是负数被拆分了'''30             calc_list[num+1] = calc_list[num] + calc_sign[num] + calc_list[num+1]31             del calc_sign[num]32             del calc_list[num]33     '''下面进行乘除的正式运算,上面都是进行格式转换'''34     for index,string in enumerate(calc_list):35         '''首先提取编号,便于后面替换运算出来的值'''36         if "/" in string or "*" in string:37             mul_div_sign = re.findall("[/*]",string)38             mul_div_list = re.split("[/*]",string)39             calc_value = None40             for e_index,e in enumerate(mul_div_list):41                 if calc_value:42                     if mul_div_sign[e_index-1] == "/":43                         calc_value /= float(e)44                     elif mul_div_sign[e_index-1] == "*":45                         calc_value *= float(e)46                 else:47                     calc_value = float(e)48                 calc_list[index] = calc_value49         else:50             pass51     '''计算值'''52     value = None53     for k,v in enumerate(calc_list):54         '''计算加减的情况'''55         if value:56             if calc_sign[k-1] == "-":57                 value -= float(v)58             elif calc_sign[k-1] == '+':59                 value += float(v)60         else:61             value = float(v)62     return value63 64 65 def main(formula):66     '''程序主入口,生成带括号的情况'''67     while True:68         formula_deep = re.search("\(.[^()]+\)",formula)69         if formula_deep:70             formula_deep = formula_deep.group()71             formula_list = modify(formula_deep)72             '''得到修整好要计算的字符串,现在开始进行计算-9-2*5/-3+7/3*99/4*2998+10*568/14'''73             calc_value = multiply_divide(formula_list)74             formula = formula.replace(formula_deep,str(calc_value))75         else:76             '''处理不带括号的情形'''77             formula = modify(formula)78             calc_last_value = multiply_divide(formula)79             print("formula:",calc_last_value)80             exit()81 82 if __name__=="__main__":83     main(formula)

 

    程序运行过程:

  大致思路:我们知道,要计算上面字符串的格式,可以使用eval()函数,但是这里我们要自己编写一个计算器;我们知道,数学运算的优先级是括号的优先级最高,先运算括号内的东西,因此我们的思路是匹配出内存的括号,然后进行运算,当把内存括号中的内容匹配出来之后,我们计算,然后用计算出来的值替换字符串中原来位置的值,一直替换到字符串中没有括号位置,这个时候我们就按照正常的运算顺序来进行计算。

    1、正则匹配,先找到内存括号;代码如下:

    formula = '1 - 2 * ( (60-30 +(-9-2*5/-3 + 7 /3*99/4*2998 +10 * 568/14 ) * (-40/5)) - (-4*3)/ (16-3*2) )'
  formula_deep = re.search("\(.[^()]+\)",formula)
  print(formula_deep.group())

    运行结果如下:

    (-9-2*5/-3 + 7 /3*99/4*2998 +10 * 568/14 )

    上面我们观察提取出来内存的字符串,可以看出,有很多地方需要修饰一下,字符串中有很多空,这会影响我们计算,还有在计算的过程中我们不需要括号,因此也要去掉括号。

    2、去除空格和括号;

    formula_deep = re.sub("[() ]","",formula_deep)

    运行如下:

    -9-2*5/-3+7/3*99/4*2998+10*568/14
    3、得到上述字符串之后,我们在逐层运算的时候,可以会出现+-,-+,++,--等情况,这个时候也要进行处理,由于现在是第一层,看不出来问题,当运算之后,如果内存得到的是负数,那么前面括号外面是+或-的话就变成+-,--等情况,这个时候也是不行的,也要进行处理;

    formula_deep = re.sub("\+-",'-',formula_deep)
  formula_deep = re.sub("-+",'-',formula_deep)
  formula_deep = re.sub("\++","+",formula_deep)
  formula_deep = re.sub("--",'+',formula_deep)

    运行如下:

    -9-2*5/-3+7/3*99/4*2998+10*568/14

    4、当上面处理完成之后,就正式开始计算,我们知道,计算首先要计算乘除,因为乘除的优先级最高,因为要先找到乘除,计算完成之后再计算加减;

    calc_sign = re.findall("[+-]",formula_deep)
  calc_list = re.split("[+-]",formula_deep)
  print(calc_list)
  print(calc_sign)

    运行如下:

    ['', '9', '2*5/', '3', '7/3*99/4*2998', '10*568/14']
  ['-', '-', '-', '+', '+']

    我们得到了计算的列表,和运算符号,['', '9', '2*5/', '3', '7/3*99/4*2998', '10*568/14']里面也是有问题的,首先,列表的第一个元素是""空的情形,说明前面是-号才会有这种情况出现,这样的情况是要合并的,还有2*5/,这种说明后面跟着的也是负数,也要先进行处理才能进行预案算,为什么会出现这种情况呢?这是由于在运算的过程中,我们是以"+"或"-"来分割字符串的,这样就会造成如果是一个负数,就会被分割掉,在开头的时候,前面会被分割一个空,这样就会造成我们运算出错,因为要进行修改。

    5、对上面运算进行修正:

逆波兰表达式实现的简易计算器
逆波兰表达式实现的简易计算器

逆波兰表达式实现的简易计算器

下载

    if calc_list[0] == "":
     calc_list[1] = calc_sign[0] + calc_list[1]
     del calc_list[0]
     del calc_sign[0]
  print("calc_list:",calc_list)
  print("calc_sign:",calc_sign)
  for index,e in enumerate(calc_list):
     if e.endswith("/") or e.endswith("*"):
         '''说明后面跟着的是负数,要进行修正'''
         calc_list[index+1] = calc_list[index] + calc_sign[index] + calc_list[index+1]
         del calc_list[index]
  del calc_sign[index]

  print("calc_list:",calc_list)
  print("calc_sign:",calc_sign)

    运行如下:

    calc_list: ['-9', '2*5/', '3', '7/3*99/4*2998', '10*568/14']
  calc_sign: ['-', '-', '+', '+']
  calc_list: ['-9', '2*5/-3', '7/3*99/4*2998', '10*568/14']
  calc_sign: ['-', '+', '+']

    从上面可以看出,我们分两笔进行了修正,第一次去除了开头的""空元素问题;第二次提出了乘除后面跟着负数的情况;

    6、这个时候,我们就要进行计算了,我们首先遍历calc_list: ['-9', '2*5/-3', '7/3*99/4*2998', '10*568/14']中的元素,因为我们要先进行乘除,因此找到包含"/"或"*"的字符串,进行求值,然后进行替换,就可以得到没有乘除的字符串,只包含加减情况:

for num,value in enumerate(calc_list):if "/" in value or "*" in value:"""说明包含乘除,首先进行计算"""mul_div_sign = re.findall("[/*]",value)
        mul_div_list = re.split("[*/]",value)
        print(mul_div_sign)
        print(mul_div_list)

    运算结果如下:

    ['*', '/']
  ['2', '5', '-3']
  ['/', '*', '/', '*']
  ['7', '3', '99', '4', '2998']
  ['*', '/']
  ['10', '568', '14']

    我们得到了运算符和里面的数字,现在只要判断乘除号,然后就可以利用前面一个乘以后面一个进行计算了。如下:

    7、乘除计算:

 1 for num,value in enumerate(calc_list): 2     if "/" in value or "*" in value: 3         """说明包含乘除,首先进行计算""" 4         mul_div_sign = re.findall("[/*]",value) 5         mul_div_list = re.split("[*/]",value) 6         '''接下来,我们计算乘除的情况,首先我们要遍历乘除,因为要进行元素递乘''' 7         res = None 8         for e_index,e_value in enumerate(mul_div_list): 9             if res:10                 if mul_div_sign[e_index-1] == "/":11                     res /= float(e_value)12                 elif mul_div_sign[e_index-1] == "*":13                     res *= float(e_value)14             else:15                 res = float(e_value)      #如果不存在,就生成一个新的,但是我们定义的是不存在的情况,因此肯定是先生成一个数,然后在进行计算16         calc_list[num] = res17     else:18         pass19 20 print(calc_list)21 print(calc_sign)

    运行结果如下:

    ['-9', -3.3333333333333335, 173134.50000000003, 405.7142857142857]
  ['-', '+', '+']

    上述代码,我们进行了乘除的运算,让运算里面不在存在乘除,只需要进行加减运算即可。

    可以看见,我们运算之后,只剩下了加减,这样,我们就可以利用列表的元素和符号进行加减运算。

    8、加减运算:

 1 '''进行加减运算''' 2 result = None 3 for k_index,k_value in enumerate(calc_list): 4     if result: 5         if calc_sign[k_index-1] == "+": 6             result += float(k_value) 7         elif calc_sign[k_index-1] == '-': 8             result -= float(k_value) 9     else:10         result = float(k_value)11 print("result:",result)

    运行如下:

    result: 173534.54761904766

    9、上面,我们得到了运算的结果,然后只需要替换内存括号的内容即可,这样一层一层替换,最终只会剩下没有括号的运算,这个时候,我们在这行这个函数,就能得到最终的结果。

    知识点:

    (1):

<span style="font-family: 宋体; font-size: 16px">result = <span style="color: #000080">None<br/></span><span style="color: #000080">for </span><span style="background-color: #ffe4ff">k_index</span>,k_value <span style="color: #000080">in </span><span style="color: #000080">enumerate</span>(calc_list):</span><br/><span style="font-family: 宋体; font-size: 16px">    <span style="color: #000080">if </span>result:</span><br/><span style="font-family: 宋体; font-size: 16px">        <span style="color: #000080">if </span>calc_sign[<span style="background-color: #e4e4ff">k_index</span>-<span style="color: #0000ff">1</span>] == <span style="color: #008080">"+"</span>:</span><br/><span style="font-family: 宋体; font-size: 16px">            result += <span style="color: #000080">float</span>(k_value)</span><br/><span style="font-family: 宋体; font-size: 16px">        <span style="color: #000080">elif </span>calc_sign[<span style="background-color: #e4e4ff">k_index</span>-<span style="color: #0000ff">1</span>] == <span style="color: #008080">'-'</span>:</span><br/><span style="font-family: 宋体; font-size: 16px">            result -= <span style="color: #000080">float</span>(k_value)</span><br/><span style="font-family: 宋体; font-size: 16px">    <span style="color: #000080">else</span>:</span><br/><span style="font-family: 宋体; font-size: 16px">        result = <span style="color: #000080">float</span>(k_value)</span>

    上述代码中,体现了一个思想,由于我们想实现的是前一个数字加上后一个数字,但是没有直接的方法,这个时候,我们就可以先定义一个空值,然后对这个值进行判断,其实判断的目的就是为了给这个变量赋值,赋值就是列表的第一个元素,这样我们就能实现列表中元素每次循环都进行叠加或叠减。这个思想很好。当不存在,想让它存在的时候,就先定义一个空值进行判断,判断之后在进行赋值。赋值之后,相当于result等于元素的第一个值,并且元素下一次循环也是从第二个值开始,列表的长度索引也没有超标。

    2、正则表达式的利用,re(regular的缩写):

    ^表示非,"\"表示转义,就是让表示字符本身的含义,+代表一个或多个

    "\(.[^()]+\)"代表匹配括号,括号中间是包含任意多个不是()的元素,也就是匹配最内层的括号。

    3、字符串的replace()方法,就把字符串进行查找替换,str.replace(old,new),正则中字符串findall()查找元素中所有的正则表达式的值,放在一个列表中;re.split()字符串的分割,按照某个正则字符串进行分割。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

705

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

233

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

117

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

22

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

61

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

30

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

15

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

669

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

58

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SciPy 教程
SciPy 教程

共10课时 | 1.6万人学习

R 教程
R 教程

共45课时 | 6.9万人学习

SQL 教程
SQL 教程

共61课时 | 4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号