学习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)

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


学习python之路--Day5 计算器_第1张图片
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)

你可能感兴趣的:(学习python之路--Day5 计算器)