头疼,其实这个程序在我看的视频当中是当做re模块的运用来进行测试的,而到了我这里就成了简化版的了,因为我实在是做吐了,恕小弟无能,只能做简化版的。为何说是简化版呢,因为要求是给的计算式是多层嵌套的小括号“()”,然后计算的时候不能用eval方法,eval是个好东西啊,他能直接得到字符串中的结果,比如eval(“23+34*45/345-34”)=结果,是可以直接给你返回算术式的结果的,如果你自己处理这段字符串的话就要花好多工夫拆解字符串。我的简化版是将多层小括号给分为了小括号“()”、中括号“【】”、大括号“{}”,不允许使用多层相同括号连续嵌套(相应的不允许超过三层,超过三层,小弟无能为力),而我觉得,想要区分不同的层级的小括号“(()())”是需要下功夫用re正则去匹配的,而我这个用是用到正则了,但用的很少,重点全在如何自己写代码实现eval的功能了。
效果图:
另附一张各步骤计算详解
源代码:
import re def Get_FuH(As):
As = As.replace('--','+')
As = As.replace('+-', '+')
As = As.replace('/-', '/')
As = As.replace('*-', '*')
End = []
As = list(As)
if (As[1] == '-'):
As.pop(1)
for i in As:
if(i == '/' or i == '+' or i == '*' ):
End.append(i)
elif(i == '-'):
End.append('+')
for i in End:
if(i == ''):
End.pop(End.index('')) return End
def Get_Num(As):
As = As.replace('--', '-')
As = As.replace('+-', '-')
As = As.replace('/-', '-')
As = As.replace('*-', '-')
As = list(As)
Get = []
End = []
for i in As:
if(i <= "9" and i >= "0" or i == '.'):
Get.append(i)
else:
End.append(''.join(Get))
Get = []
if(i == '-'):Get.append(i)
for i in End:
if(i == ''):
End.pop(End.index(''))
for i in End:
if(i == ''):
End.pop(End.index(i))
return End def Do_Xiao(As):
a = re.findall("\([+\-*/0-9.]*\)",As)
# print(a)
for i in a:
TiH = i
ShuZ = Get_Num(TiH)
FuH = Get_FuH(TiH)
ShuZ_New = []
FuH_New = []
# print(TiH)
# print(ShuZ)
# print(FuH)
for q in FuH:
if (q == '*'):
m = FuH.index('*')
End = float(ShuZ[m]) * float(ShuZ[m + 1])
ShuZ[m] = 0
FuH[m] = 0
ShuZ[m+1] = End
elif (q == '/'):
m = FuH.index('/')
End = float(ShuZ[m]) / float(ShuZ[m + 1])
ShuZ[m] = 0
FuH[m] = 0
ShuZ[m + 1] = End
for i in ShuZ:
if (i != 0): ShuZ_New.append(i)
for i in FuH:
if (i != 0): FuH_New.append(i)
# print(ShuZ_New)
# print(FuH_New)
for i in FuH_New:
if (i == '+'):
n = FuH_New.index('+')
End = float(ShuZ_New[n]) + float(ShuZ_New[n + 1])
ShuZ_New[n] = 0
FuH_New[n] = 0
ShuZ_New[n + 1] = float(End)
elif (i == '-'):
n = FuH_New.index('-')
End = float(ShuZ_New[n]) - float(ShuZ_New[n + 1])
ShuZ_New[n] = 0
FuH_New[n] = 0
ShuZ_New[n + 1] = float(End)
for p in ShuZ_New:
if (p != 0):
As = As.replace(TiH, str(p))
return As
def Do_Zhong(As):
As = As.replace("[","(")
As = As.replace("]",")")
As = Do_Xiao(As)
return As def Do_Da(As):
As = As.replace("{","(")
As = As.replace("}",")")
# print(As)
As = Do_Xiao(As)
return As def End_end(As):
As = '(' + As + ')'
End = Do_Xiao(As)
return End if __name__ == '__main__':
AA = "1-2*{[60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14)]-(-4*3)/(16-3*2)}"
BB = "34+45*{44+34*45+44/45}-{[34*45/44]*(456+466*345-3445/455)}"
As = input("请输入你要计算的式子:")
As = Do_Xiao(As)
print(As)
As = Do_Zhong(As)
print(As)
As = Do_Da(As)
print(As)
End = End_end(As)
print(End) 解析:我的整体思路很简单,就是按顺序一步步提取,先用正则把小括号的内容提取出来,因为有格式要求,所以提取的小括号的内容都是闭合的(即()中间不会出现()或者单边括号,匹配较为容易)。对提取的内容进行处理:定义了几个函数:1、提取数字,我将提取的字符串转为列表,然后把通过运算符分割的数字(之所以取运算符是因为有多位数还有小数点的存在,这样可以将一个数字完整的存放在一起)按个存放到一个列表中。2、提取运算符,直接扫描字符串列表提取即可。3、运算,因为运算符是两个数进行运算,所以通过循环运算符,每一个运算符都是运算符左右两个数的和。为了区分优先级(先乘除后加减),我进行了两边循环符号,且第二遍循环是在第一次的基础上删除了乘除运算符后的列表。即第一遍循环只算乘除,第二遍再算加减。4、中括号转小括号,一定要在小括号运算完,公式中没有小括号才能调用。5、大括号转小括号,同理中括号,因为如果大括号转成小括号,里面还有中括号会卡在中括号上没有匹配符。
再说说中间的遇到的一些bug,也是导致代码混乱的地方,这些我都没来的及优化,测视了两个算术式没问题就不搞了,头疼。1、提取数字,“-”是个大问题,到底把他算在运算符列表中呢还是算在数字列表中呢,我选择的是放在数字中,即数字分正负,由此我需要判断“+-”、“--”、“/-”、“*-”,后两个还好说,前两个真是搞死了,就在那算负负、正正的逻辑去了,还要在表达式中进行替换工作,将双符号进行缩减成标准的单符号传进符号列表中,因为符号列表的数目一旦不对,就会导致数字列表没有匹配正确(这也是最麻烦的地方,中间出了好几次超出列表范围的bug)。另一个问题比较严重的问题是在乘除算完给加减新的列表的时候,因为有连乘连除的存在,所以我需要将计算的结果再存到列比中循环利用,我的方法是算一个弹出一个,既可以继续运行,还可以缩减列表,到最后符号列表为空,数字列表还剩一个结果。呵呵,理想很丰满,结果最大的bug出来了,找了好半天,把所有的循环都输出了一遍才找到原因:因为我是用for循环,通过i in 符号列表,然后当i匹配到运算符,就找到这个下表,然后弹出去这个运算符元素,数字列表弹出去两个数字,但当我符号弹出去这个后,列表索引自动迁移,导致i+1成了i,i+1还没进行循环,就变成了下一次循环到了i+2,简单说就是每一次循环i+1,当我弹出当前位置i后,i(y) = i+1,且这是在本次循环结束前,所以当下一次循环开始时,其实是y+1,也就是上一次循环中的i(i已经被弹出去了,i成了i+1)的后面的后面这个元素,及跳过了一个i的循环,为了避免这种情况,我不弹出元素,而是全都初始化为0,当乘除运算完后,再进行“除0”,即构造新列表再次进行加减运算。
使用说明:大、中、小括号必须数目,位置等对称,且不能乱套,中规中举的小中大或者小中、小大、中大,不可小套大。式子中用到那种括号就调用一次对应的函数,且从小往大一层层的调。我没有设检查式子是否合规,讲道理应该不会再出什么问题了,AA的式子是我照着这个写的程序,BB是用来测试的,我只测试了这一个,毕竟长公式写起来也麻烦,自己再在手机上算一遍也麻烦,但AA、BB的式子我都在手机上算过了,都对,且更精确。