一,需求分析
要求计算一串包含数字+-*/()的类似于3*( 4+ 50 )-(( 100 + 40 )*5/2- 3*2* 2/4+9)*((( 3 + 4)-4)-4)表达式的数值
二,知识点
- 正则表达式
- 元素切片
- 函数
- 函数递归
三,流程分析
- 判断字符串是否包含()如果有先处理()内表达式并且计算结果返回新字符串
- 不包含()则直接计算表达式就先判断是否包含*/
- 先计算*/再计算+-返回结果
流程图如下
四,实现过程
1,正则表达式处理用户输入的字符串
cal1.py
import re expression = '100.5+40*5/2-3*2*2/4+9' #d代表数字.代表数字意义上的小数点[d.]+表示可以匹配一个数字或者小数点.组成的字符串及可以代表所有数字 #匹配+及*需要加转义 #匹配-/因为本身没有特殊函数不需要转义 l = re.findall('[d.]+|+|-|*|/',expression) #l=['100.5', '+', '40', '*', '5', '/', '2', '-', '3', '*', '2', '*', '2', '/', '4', '+', '9']
2,首先定义出不包含()的字符串的计算方法
cal2.py
import re expression = '100.5+40*5/2-3*2*2/4+9' #d代表数字.代表数字意义上的小数点[d.]+表示可以匹配一个数字或者小数点.组成的字符串及可以代表所有数字 #匹配+及*需要加转义 #匹配-/因为本身没有特殊函数不需要转义 l = re.findall('[d.]+|+|-|*|/',expression) #l=['100.5', '+', '40', '*', '5', '/', '2', '-', '3', '*', '2', '*', '2', '/', '4', '+', '9'] #定义乘除法运算函数 def multdiv(l,x): #l是转换后的列表,x是传递的*或者/ #首先找到第一个*或者/的位置,需要计算的即为位置前后的两个数 #本列子a = 2 l[a-1] = 40 l[a+1] = 5 a = l.index(x) if x == '*': k = float(l[a-1]) * float(l[a+1]) else: k = float(l[a - 1]) / float(l[a + 1]) #依次删除找到的符号连同前后的两个元素 #本列子删除的是'40','*','5' del l[a-1],l[a-1],l[a-1] #把计算结果转换成字符在插入列表原位置这样就完成了一次乘法或者除法运算 #并且把结果返回生成新的列表 l.insert((a-1),str(k)) #调用多次该函数就会把*/计算出来后返回一个不包含*/的新列表 return l #定义计算函数,其中需要调用multdiv函数去除*/ def fun(l): #函数需要传递的参数为一个列表 #定义函数需要返回的结果 sum = 0 #假如列表不为空,无限循环处理列表 while l: #先判断列表是否包含*/执行对应的运算 #如果列表里面只有*没有/则把'*'作为实参传递给处理乘除法的函数multdiv if '*' in l and '/' not in l: multdiv(l,'*') #同理处理只包含/符号的 elif '/' in l and '*' not in l: multdiv(l,'/') #如果列表里面既包含*又包含/则先取到对应的索引值,比较大小后先处理前面再处理后面的 elif '*' in l and '/' in l: a = l.index('*') b = l.index('/') if a < b: multdiv(l,'*') else: multdiv(l,'/') #到这里已经处理完*/列表只剩下数字和+- else: #考虑到处理完*/以后的列表第一位可能是- #先把索引位置0 1 合并 #例如l = ['-','1'...] #合并后l = ['-1'] if l[0] == '-': l[0] = l[0] + l[1] del l[1] #处理完以后的列表只包含数字+ - #把列表第一位赋值给sum作为起始需要计算的基数 sum += float(l[0]) #循环处理列表,因为列表的格式类似于['1','+','3','-','-4'] #所以需要取l[1] l[3] ...判断运算符号是+还是-然后根据情况进行累计加减 for i in range(1,len(l),2): if l[i] == '+': sum += float(l[i+1]) elif l[i] == '-': sum -= float(l[i+1]) break return sum print(100.5+40*5/2-3*2*2/4+9) #206.5 a = fun(l) print(a) #206.5
这样处理的列表还有一个问题,假如需要处理的字符串有一串是这样的格式7*((1-4)-4) 使用fun计算一次为7*(-3-4) 再处理一次为7*(-7) 这样的字符串分割成列表为['7','*','-','7']需要在定义乘除以及加减方法的时候可以处理这种情况
cal3.py
import re expression = '100.5+40*5/2-3*2*2/4+9' #d代表数字.代表数字意义上的小数点[d.]+表示可以匹配一个数字或者小数点.组成的字符串及可以代表所有数字 #匹配+及*需要加转义 #匹配-/因为本身没有特殊函数不需要转义 l = re.findall('[d.]+|+|-|*|/',expression) #l=['100.5', '+', '40', '*', '5', '/', '2', '-', '3', '*', '2', '*', '2', '/', '4', '+', '9'] #定义乘除法运算函数 def multdiv(l,x): #l是转换后的列表,x是传递的*或者/ #首先找到第一个*或者/的位置,需要计算的即为位置前后的两个数 #本列子a = 2 l[a-1] = 40 l[a+1] = 5 a = l.index(x) if x == '*' and l[a+1] != '-': k = float(l[a-1]) * float(l[a+1]) elif x == '/' and l[a+1] != '-': k = float(l[a - 1]) / float(l[a + 1]) #如果*或者/后面对应的是-号则计算时候需要在前面加- elif x == '*' and l[a+1] == '-': k = -float(l[a - 1]) * float(l[a + 2]) elif x == '/' and l[a+1] == '-': k = -float(l[a - 1]) / float(l[a + 2]) #依次删除找到的符号连同前后的两个元素 #本列子删除的是'40','*','5' del l[a-1],l[a-1],l[a-1] #把计算结果转换成字符在插入列表原位置这样就完成了一次乘法或者除法运算 #并且把结果返回生成新的列表 l.insert((a-1),str(k)) #调用多次该函数就会把*/计算出来后返回一个不包含*/的新列表 return l #定义计算函数,其中需要调用multdiv函数去除*/ def fun(l): #函数需要传递的参数为一个列表 #定义函数需要返回的结果 sum = 0 #假如列表不为空,无限循环处理列表 while l: #先判断列表是否包含*/执行对应的运算 #如果列表里面只有*没有/则把'*'作为实参传递给处理乘除法的函数multdiv if '*' in l and '/' not in l: multdiv(l,'*') #同理处理只包含/符号的 elif '/' in l and '*' not in l: multdiv(l,'/') #如果列表里面既包含*又包含/则先取到对应的索引值,比较大小后先处理前面再处理后面的 elif '*' in l and '/' in l: a = l.index('*') b = l.index('/') if a < b: multdiv(l,'*') else: multdiv(l,'/') #到这里已经处理完*/列表只剩下数字和+- else: #考虑到处理完*/以后的列表第一位可能是- #先把索引位置0 1 合并 #例如l = ['-','1'...] #合并后l = ['-1'] if l[0] == '-': l[0] = l[0] + l[1] del l[1] #处理完以后的列表只包含数字+ - #把列表第一位赋值给sum作为起始需要计算的基数 sum += float(l[0]) #循环处理列表,因为列表的格式类似于['1','+','3','-','-4'] #所以需要取l[1] l[3] ...判断运算符号是+还是-然后根据情况进行累计加减 for i in range(1,len(l),2): if l[i] == '+' and l[i+1] != '-': sum += float(l[i+1]) elif l[i] == '-' and l[i+1] != '-': sum -= float(l[i+1]) #如果列表中+后面对应的是-则再推后一位计算- elif l[i] == '+' and l[i+1] == '-': sum -= float[l[i+2]] #如果列表中-号后面对应的是-则再推后一位--得+ 计算+ elif l[i] == '-' and l[i + 1] == '-': sum += float[l[i+2]] #循环完毕列表退出整个循环 break #把计算的结果sum作为返回值返回 return sum print(100.5+40*5/2-3*2*2/4+9) #206.5 a = fun(l) print(a) #206.5
以上程序可以处理不带任何括号的字符串,下面新加一个函数处理带有括号的字符串
cal4.py
import re expression = '100.5+40*5/2-3*2*2/4+9' #d代表数字.代表数字意义上的小数点[d.]+表示可以匹配一个数字或者小数点.组成的字符串及可以代表所有数字 #匹配+及*需要加转义 #匹配-/因为本身没有特殊函数不需要转义 l = re.findall('[d.]+|+|-|*|/',expression) #l=['100.5', '+', '40', '*', '5', '/', '2', '-', '3', '*', '2', '*', '2', '/', '4', '+', '9'] #定义乘除法运算函数 def multdiv(l,x): #l是转换后的列表,x是传递的*或者/ #首先找到第一个*或者/的位置,需要计算的即为位置前后的两个数 #本列子a = 2 l[a-1] = 40 l[a+1] = 5 a = l.index(x) if x == '*' and l[a+1] != '-': k = float(l[a-1]) * float(l[a+1]) elif x == '/' and l[a+1] != '-': k = float(l[a - 1]) / float(l[a + 1]) #如果*或者/后面对应的是-号则计算时候需要在前面加- elif x == '*' and l[a+1] == '-': k = -float(l[a - 1]) * float(l[a + 2]) elif x == '/' and l[a+1] == '-': k = -float(l[a - 1]) / float(l[a + 2]) #依次删除找到的符号连同前后的两个元素 #本列子删除的是'40','*','5' del l[a-1],l[a-1],l[a-1] #把计算结果转换成字符在插入列表原位置这样就完成了一次乘法或者除法运算 #并且把结果返回生成新的列表 l.insert((a-1),str(k)) #调用多次该函数就会把*/计算出来后返回一个不包含*/的新列表 return l #定义计算函数,其中需要调用multdiv函数去除*/ def fun(s): #函数需要传递的参数为一个字符串 #需要把字符串转换成列表 l = re.findall('[d.]+|+|-|*|/', s) #定义函数需要返回的结果 sum = 0 #假如列表不为空,无限循环处理列表 while l: #先判断列表是否包含*/执行对应的运算 #如果列表里面只有*没有/则把'*'作为实参传递给处理乘除法的函数multdiv if '*' in l and '/' not in l: multdiv(l,'*') #同理处理只包含/符号的 elif '/' in l and '*' not in l: multdiv(l,'/') #如果列表里面既包含*又包含/则先取到对应的索引值,比较大小后先处理前面再处理后面的 elif '*' in l and '/' in l: a = l.index('*') b = l.index('/') if a < b: multdiv(l,'*') else: multdiv(l,'/') #到这里已经处理完*/列表只剩下数字和+- else: #考虑到处理完*/以后的列表第一位可能是- #先把索引位置0 1 合并 #例如l = ['-','1'...] #合并后l = ['-1'] if l[0] == '-': l[0] = l[0] + l[1] del l[1] #处理完以后的列表只包含数字+ - #把列表第一位赋值给sum作为起始需要计算的基数 sum += float(l[0]) #循环处理列表,因为列表的格式类似于['1','+','3','-','-4'] #所以需要取l[1] l[3] ...判断运算符号是+还是-然后根据情况进行累计加减 for i in range(1,len(l),2): if l[i] == '+' and l[i+1] != '-': sum += float(l[i+1]) elif l[i] == '-' and l[i+1] != '-': sum -= float(l[i+1]) #如果列表中+后面对应的是-则再推后一位计算- elif l[i] == '+' and l[i+1] == '-': sum -= float(l[i+2]) #如果列表中-号后面对应的是-则再推后一位--得+ 计算+ elif l[i] == '-' and l[i + 1] == '-': sum += float(l[i+2]) #循环完毕列表退出整个循环 break #把计算的结果sum作为返回值返回 return sum #定义最终函数计算带有()的字符串 def calculate(expression): #函数传递的是一个字符串类似于(2-1)+(4-3)*3 #定义函数的最终返回值及计算结果 ans = 0 ex = [] #如果字符串不包含(则代表处理完()直接调用函数fun返回结果即可 if '(' not in expression: ans = fun(expression) return ans #否则处理() else: #使用search查找字符串中出现的第一个最里面的() #([^()]+)的意思是查找以(开始以)结尾的并且中间不包含任何()的字符串返回一个对象 #该对象有取出的字符串以及该字符串对应的索引 ret = re.search('([^()]+)',expression) temp = ret.group() #temp = '(1+2)' ex = ret.span() #ex = (0,5) #切片去除首尾的()剩下不含()的字符串 sub = temp[1:-1] #sub = '1+2' #把不包含()的字符串作为参数传递给fun函数计算结果 k = fun(sub) #k = 3 #这样字符串被分为三段分别赋值给l1,l2,l3,重新拼接一下 l1 = expression[0:ex[0]] #l1='' l2 = str(k) #l2='1' l3 = expression[ex[1]:] #l3='+(4-3)*3' expression = l1 + l2 + l3 #第一次处理完()拼接的结果为 #expression = '3+(4-3)*3' #递归调用函数第二次拼接后的结果是 #expression = '3+-1*3' #以此类推直到没有() return calculate(expression) a = calculate('1+(2-3)*5') print(a)