学习python之路--Day5 计算器

需求

可以处理带括号的加减乘除运算

需求分析

匹配括号
re.search('\(.*\)',a)

匹配最里面括号
re.search(r'\([^()]+\)')

可以用strip()方法去掉括号

计算公式
1.0-2.0((60.0-30.0+(-40.0/5.0)(9.0-2.05.0/3.0+7.0/3.099.0/4.02998.0+10.0568.0/14.0)))

匹配二级括号
re.search('\(\(.*\)\)',a)

确定以上方法之后我们先来画流程图


calculator

实现

可以看到我们的逻辑是用re.findall(r'\([^()]+\)')判断是否存在括号,如果存在,我们就进入处理([^()]+)最里面括号的内容
这是一个循环,直到处理的没有([^()]+)这一项,也就代表没有括号内的内容了
如果一开始就没有匹配到([^()]+),那我们就直接按加减乘除的规则处理他
一开始的循环可以这么写:

loop_flag2=True
while loop_flag2:
        if re.findall('\([^()]+\)',enter):
                enter=three_level_brackets_func(enter)
        else:
                loop_flag2=False
else:
        finnal_result=no_brackets_func(enter)
        print(finnal_result)

然后按着流程图,先处理高级运算符,也就是乘法和除法运算符,我们先处理除法,这是因为在实际操作中,除法必须按从左到右的顺序运算,除法运算符先算左边的和先算右边的结果,是天差地别的变化,乘法还好,先乘左边的还是先乘右边的结果都是一样,请看下面的列子:

>>> 3/4/5
0.15
>>> 3/(4/5)
3.75

#乘法
>>> 3*4*5
60
>>> 3*(4*5)
60

除法这部分的逻辑如下:

while True:
    if operation.find('/') != -1:
                        division_cal=re.search('[0-9]+.[0-9]+/[\+\-]?[0-9]+.[0-9]+',operation).group()
                        num1=re.findall('([0-9]+.[0-9]+)/',operation)
                        num2=re.findall('/([\+\-]?[0-9]+.[0-9]+)',operation)
                        print('division left:',num1)
                        print('division right:',num2)
                        num1=float(num1[0])
                        num2=float(num2[0])
                        division_result=num1/num2
                        print('division result:',division_result)
                        operation=operation.replace(division_cal,str(division_result))
                        print('now operation is:',operation)

如果我们发现运算行里有'/'这个符号,代表除法,那么我们就把运算行第一个除法公式提取出来,用division_cal=re.search('[0-9]+.[0-9]+/[\+\-]?[0-9]+.[0-9]+',operation).group(),匹配出来的是[0-9]+.[0-9]+/[+-]?[0-9]+.[0-9]+,就是两个有小数点的数字中间是 / 除法运算符,例如34.5/-55.6这种。
然后把除法运算符左边的数字拿出来num1=re.findall('([0-9]+.[0-9]+)/',operation),是一个列表,会把所有'/'左边的数字拿出来
再把除法运算符右边的数字拿出来num2=re.findall('/([\+\-]?[0-9]+.[0-9]+)',operation),同理也是一个列表
num1=float(num1[0]) num2=float(num2[0])
我们算两个列表里第一位的数字,因为两个列表里项目是一一匹配的(如果输入的运算式正确的话),两个列表的第一位就代表他们是这个运算行第一个除法运算
算出的结果,用这个结果替换第一个除法公式 operation=operation.replace(division_cal,str(division_result))
这个运算行就少了一个除法公式
再循环,直到没有任何除法公式
接着,我们把没有除法公式了的运算行输入乘法函数里处理。目的是把所有乘法公式也替换掉
逻辑如下:

elif operation.find('*') != -1:
                        mutlip_cal=re.search('[0-9]+.[0-9]+\*[\+\-]?[0-9]+.[0-9]+',operation).group()
                        num1=re.findall('([0-9]+.[0-9]+)\*',operation)
                        num2=re.findall('\*([\+\-]?[0-9]+.[0-9]+)',operation)
                        print('multiplication left:',num1)
                        print('multiplication right:',num2)
                        num1=float(num1[0])
                        num2=float(num2[0])
                        mutlip_result=num1*num2
                        print('multiplication result:',mutlip_result)
                        operation=operation.replace(mutlip_cal,str(mutlip_result))
                        print('now operation is:',operation)

跟除法的规则一样,先找到第一个乘法公式,然后提取乘法符号'*'左边和右边的数字,相乘出结果,替换公式,再循环,直到没有任何乘法

如果乘法和除法的处理都做完了,那接下去可以想象,就只剩下加减了

while loop_flag:
                new_operation=operation.strip('()')
                if len(re.split('[+-]',new_operation)) > 1 and re.split('[+-]',new_operation)[0]:
                        pri_cal=re.search('[\+\-]?[0-9]+.[0-9]+[\+\-][0-9]+.[0-9]+',new_operation).group()
                        num1=re.findall('([\+\-]?[0-9]+.[0-9]+)[\+\-]',pri_cal)
                        num2=re.findall('[\+\-]?[0-9]+.[0-9]+([\+\-][0-9]+.[0-9]+)',pri_cal)
                        num1=float(num1[0])
                        num2=float(num2[0])
                        pri_result=num1+num2
                        operation=operation.replace(pri_cal,str(pri_result))
                        print('now operation is:',operation)

之前的运算可能还留下括号没有处理,我们用new_operation=operation.strip('()')这一句来去掉左右括号
if len(re.split('[+-]',new_operation)) > 1
这是判断除了'+-'号之外,还有几项数字,如果大于1,那就是还需要运算,当然这样就有个漏洞,如果是'-34.5'这种数字,re.split('[+-]','-34.5')这种方法分裂出来的项目是[],[34.5] 这样也是大于1项的,所以我们加一句
if re.split('[+-]',new_operation)[0]:
来判断分裂出来的第一位是不是空
接下来,pri_cal=re.search('[\+\-]?[0-9]+.[0-9]+[\+\-][0-9]+.[0-9]+',new_operation).group() 提取出第一个加减运算公式
num1=re.findall('([\+\-]?[0-9]+.[0-9]+)[\+\-]',pri_cal) 提取左边的数字
num2=re.findall('[\+\-]?[0-9]+.[0-9]+([\+\-][0-9]+.[0-9]+)',pri_cal 提取右边的数字
运算,因为我们匹配右边数字的时候已经把加减号也匹配进去了,python是支持1+-1这样运算的,所以我们运算直接就是把两个值加一起
operation=operation.replace(pri_cal,str(pri_result)) 拿运算结果替换加减运算式
循环

这里可能会遇到这么几种情况,一个是,经过一轮处理之后 34-(-5*11) 这种变成了34--55这种公式,到我们加减处理的逻辑里就无法匹配了,还有34+-55,34--55这种情况出现,[+-]?[0-9]+.[0-9]+([+-][0-9]+.[0-9]+匹配里也没有,当然我们也可以中间的[+-]写成[+-]{1,2}这种匹配一到两次的,但是接下去的运算可不能成功算出34+--55的结果,所以单独做个函数把这些不合法的运算符处理下

def symbol_judge(enter):
        if '+-' in enter:
                enter=enter.replace('+-','-')
        elif '-+' in enter:
                enter=enter.replace('-+','-')
        elif '--' in enter:
                enter=enter.replace('--','+')
        return enter

完善一下之前的循环

def process(enter,re_result):
        for i in re_result:
                pre_symbol_result=pre_symbol_cal(i)
                pri_symbol_result=pri_symbol_cal(pre_symbol_result)
                pri_symbol_result=pri_symbol_result.strip('()')
                enter=enter.replace(i,pri_symbol_result)
        return enter

def three_level_brackets_func(enter):
        re_result=re.findall(r'\([^()]+\)',enter)
        print('brackets inner:',re_result)
        enter=process(enter,re_result)
        enter=symbol_judge(enter)
        print(enter)
        return enter

loop_flag2=True
while loop_flag2:
        if re.findall('\([^()]+\)',enter):
                enter=three_level_brackets_func(enter)
        else:
                loop_flag2=False
else:
        finnal_result=no_brackets_func(enter)
        print(finnal_result)

先处理括号内的内容,按照先乘除,再加减,结果去除左右括号,替换运算行里的(^[]+)内容,最后再对运算行里的不合法符号做个判断,传给开始的循环,再走一轮

终于我们的运算式里没有了括号,接着就该直接按先乘除后加减的四则运算逻辑,处理它们了

def no_brackets_func(enter):
        print('HIT no_brackets')
        if enter[0] == '-':
                enter='0'+enter
        raw_enter=pre_symbol_cal(enter)
        raw_enter=symbol_judge(raw_enter)
        enter=pri_symbol_cal(raw_enter)
        print(enter)
        return enter

这里面有个小细节,如果运算式开头是-34/55+66...这样子的,咱们之前写的逻辑就要再改了,好多地方也要调整,为了方便复用之前的代码,也是偷懒,其实只用在'-'号前面加一个0即可,逻辑走得通,同时不影响结果
好了,完整的代码如下:

#-*- coding:utf-8 -*-
#owner:houyizhong
import re
print('输入数字必须保留小数点一位\n例子:1.0-2.0*((60.0-30.0+(-40.0/5.0)*(9.0-2.0*5.0/3.0+7.0/3.0*99.0/4.0*2998.0+10.0*568.0/14.0))+(65.0+77.0)+6.0)')
enter=input('>>>')
def pre_symbol_cal(operation):
    loop_flag=True
    while loop_flag:
        if operation.find('/') != -1:
            division_cal=re.search('[0-9]+.[0-9]+/[\+\-]?[0-9]+.[0-9]+',operation).group()
            num1=re.findall('([0-9]+.[0-9]+)/',operation)
            num2=re.findall('/([\+\-]?[0-9]+.[0-9]+)',operation)
            print('division left:',num1)
            print('division right:',num2)
            num1=float(num1[0])
            num2=float(num2[0])
            division_result=num1/num2
            print('division result:',division_result)
            operation=operation.replace(division_cal,str(division_result))
            print('now operation is:',operation)
        elif operation.find('*') != -1:
            mutlip_cal=re.search('[0-9]+.[0-9]+\*[\+\-]?[0-9]+.[0-9]+',operation).group()
            num1=re.findall('([0-9]+.[0-9]+)\*',operation)
            num2=re.findall('\*([\+\-]?[0-9]+.[0-9]+)',operation)
            print('multiplication left:',num1)
            print('multiplication right:',num2)
            num1=float(num1[0])
            num2=float(num2[0])
            mutlip_result=num1*num2
            print('multiplication result:',mutlip_result)
            operation=operation.replace(mutlip_cal,str(mutlip_result))
            print('now operation is:',operation)
        else:
            loop_flag=False
    return operation
def pri_symbol_cal(operation):
    loop_flag=True
    while loop_flag:
        new_operation=operation.strip('()')
        if len(re.split('[+-]',new_operation)) > 1 and re.split('[+-]',new_operation)[0]:
            pri_cal=re.search('[\+\-]?[0-9]+.[0-9]+[\+\-][0-9]+.[0-9]+',new_operation).group()
            num1=re.findall('([\+\-]?[0-9]+.[0-9]+)[\+\-]',pri_cal)
            num2=re.findall('[\+\-]?[0-9]+.[0-9]+([\+\-][0-9]+.[0-9]+)',pri_cal)
            num1=float(num1[0])
            num2=float(num2[0])
            pri_result=num1+num2
            operation=operation.replace(pri_cal,str(pri_result))
            print('now operation is:',operation)
        else:
            loop_flag=False
    return operation
def symbol_judge(enter):
    if '+-' in enter:
        enter=enter.replace('+-','-')
    elif '-+' in enter:
        enter=enter.replace('-+','-')
    elif '--' in enter:
        enter=enter.replace('--','+')
    return enter
def process(enter,re_result):
    for i in re_result:
        pre_symbol_result=pre_symbol_cal(i)
        pri_symbol_result=pri_symbol_cal(pre_symbol_result)
        pri_symbol_result=pri_symbol_result.strip('()')
        enter=enter.replace(i,pri_symbol_result)
    return enter
def three_level_brackets_func(enter):
    re_result=re.findall(r'\([^()]+\)',enter)
    print('brackets inner:',re_result)
    enter=process(enter,re_result)
    enter=symbol_judge(enter)
    print(enter)
    return enter
    
def no_brackets_func(enter):
    print('HIT no_brackets')
    if enter[0] == '-':
        enter='0'+enter
    raw_enter=pre_symbol_cal(enter)
    raw_enter=symbol_judge(raw_enter)
    enter=pri_symbol_cal(raw_enter)
    print(enter)
    return enter
loop_flag2=True
while loop_flag2:
    if re.findall('\([^()]+\)',enter):
        enter=three_level_brackets_func(enter)
    else:
        loop_flag2=False
else:
    finnal_result=no_brackets_func(enter)
    print(finnal_result)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • 第一章数和数的运算 一概念 (一)整数 1整数的意义 自然数和0都是整数。 2自然数 我们在数物体的时候,用来表示...
    meychang阅读 2,592评论 0 5
  • TF API数学计算tf...... :math(1)刚开始先给一个运行实例。tf是基于图(Graph)的计算系统...
    MachineLP阅读 3,451评论 0 1
  • 定点小数运算 来自:http://www.eepw.com.cn/article/17893.htm 在DSP世界...
    郝宇峰阅读 9,061评论 0 2
  • 今天早上晰晰尿床把自己尿醒了,五点就开始玩,早上起床先把晨读做了。看书半小时。 针灸的时候带着晰晰玩自拍,给小孩玩...
    晰晰麻麻阅读 243评论 0 0