Python实践之路7——计算器
一、代码需求
开发一个简单的Python计算器:
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) )等类似公式后,
必须自己解析里面的(),+,-,*,/符号和公式(不能调用eval等类似功能偷懒实现),结果必须与真实的计算器所得出的结果一致。
二、程序代码
1 #!/user/bin/env ptyhon 2 # -*- coding:utf-8 -*- 3 # Author: VisonWong 4 5 import re 6 7 8 def compute_mul_div(arg): 9 """ 操作乘除 10 :param expression:表达式 11 :return:计算结果 12 """ 13 14 val = arg[0] 15 #第一组形如 -40.0/5 可以匹配到 40.0/5 16 #匹配方法解释:\d+\.*\d* 可以匹配到任意实数和小数 [\*\/]可以匹配到乘除号 [\+\-]?\d+\.*\d*可以匹配到任意带符号的任意实数和小数 17 mch = re.search('\d+\.*\d*[\*\/][\+\-]?\d+\.*\d*', val) 18 if not mch: 19 return 20 content = re.search('\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*', val).group() 21 22 if len(content.split('*')) > 1: 23 n1, n2 = content.split('*') 24 value = float(n1) * float(n2) 25 else: 26 n1, n2 = content.split('/') 27 value = float(n1) / float(n2) 28 29 before, after = re.split('\d+\.*\d*[\*\/]+[\+\-]?\d+\.*\d*', val, 1) 30 new_str = "%s%s%s" % (before, value, after) 31 arg[0] = new_str 32 compute_mul_div(arg) 33 34 35 def compute_add_sub(arg): 36 """ 操作加减 37 :param expression:表达式 38 :return:计算结果 39 """ 40 while True: 41 if arg[0].__contains__('+-') or arg[0].__contains__("++") or arg[0].__contains__('-+') or arg[0].__contains__( 42 "--"): 43 arg[0] = arg[0].replace('+-', '-') 44 arg[0] = arg[0].replace('++', '+') 45 arg[0] = arg[0].replace('-+', '-') 46 arg[0] = arg[0].replace('--', '+') 47 else: 48 break 49 50 #替换符号:如出现-5+6.2-7替换为5-6.2+7,并在替换计数位加1 51 if arg[0].startswith('-'): 52 arg[1] += 1 53 arg[0] = arg[0].replace('-', '&') 54 arg[0] = arg[0].replace('+', '-') 55 arg[0] = arg[0].replace('&', '+') 56 arg[0] = arg[0][1:] 57 val = arg[0] 58 #第一组形如 40.0+5-8 可以匹配到 40.0+5 59 #匹配方法解释:\d+\.*\d* 可以匹配到任意实数和小数 [\+\-]可以匹配到乘除号 60 mch = re.search('\d+\.*\d*[\+\-]{1}\d+\.*\d*', val) 61 if not mch: 62 return 63 content = re.search('\d+\.*\d*[\+\-]{1}\d+\.*\d*', val).group() 64 if len(content.split('+')) > 1: 65 n1, n2 = content.split('+') 66 value = float(n1) + float(n2) 67 else: 68 n1, n2 = content.split('-') 69 value = float(n1) - float(n2) 70 71 before, after = re.split('\d+\.*\d*[\+\-]{1}\d+\.*\d*', val, 1) 72 new_str = "%s%s%s" % (before, value, after) 73 arg[0] = new_str 74 compute_add_sub(arg) 75 76 77 def compute(expression): 78 """ 操作加减乘除 79 :param expression:表达式 80 :return:计算结果 81 """ 82 inp = [expression, 0] 83 84 # 处理表达式中的乘除 85 compute_mul_div(inp) 86 87 # 处理表达式中的加减 88 compute_add_sub(inp) 89 90 if divmod(inp[1], 2)[1] == 1: 91 result = float(inp[0]) 92 result = result * -1 93 else: 94 result = float(inp[0]) 95 return result 96 97 98 def exec_bracket(expression): 99 """ 递归处理括号,并计算 100 :param expression: 表达式 101 :return:最终计算结果 102 """ 103 # 如果表达式中已经没有括号,则直接调用负责计算的函数,将表达式结果返回,如:2*1-82+444 104 if not re.search('\([^()]+\)', expression): 105 final = compute(expression) 106 return final 107 # 获取 第一个只含有数字/小数和操作符的括号 108 # 如: 109 # ['1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] 110 # 找出:(-40.0/5) 111 content = re.search('\([^()]+\)', expression).group() 112 113 # 分割表达式,即: 114 # 将['1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] 115 # 分割更三部分:['1-2*((60-30+( (-40.0/5) *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] 116 # 最后参数1代表仅分割第一个符合条件的部分 117 before,after = re.split('\([^()]+\)', expression, 1) 118 119 print('before:', expression) 120 content = re.sub("[()]", "", content) # 去除两边的() 121 122 # 计算,提取的表示 -40.0/5,并计算结果,即:-40.0/5=-8.0 123 ret = compute(content) 124 125 print('%s=%s' % (content, ret)) 126 127 # 将执行结果拼接,['1-2*((60-30+( -8.0 *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] 128 expression = "%s%s%s" % (before, ret, after) 129 print('after:', expression) 130 print("=" * 10, '上一次计算结束', "=" * 10) 131 132 # 循环继续下次括号处理操作,本次携带者的是已被处理后的表达式,即: 133 # ['1-2*((60-30+ -8.0 *(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))'] 134 135 # 递归调用本身,如此周而复始的操作,直到表达式中不再含有括号 136 return exec_bracket(expression) 137 138 139 # 使用 __name__ 的目的: 140 # 只有执行 python index.py 时,以下代码才执行 141 # 如果其他人导入该模块,以下代码不执行 142 if __name__ == "__main__": 143 print ('*'*20,"请计算表达式:", "1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )" ,'*'*20) 144 inpp = '1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) ) ' 145 # inpp = "1-2*-30/-12*(-20+200*-3/-200*-300-100)" 146 # inpp = "1-5*980.0" 147 148 inpp = re.sub('\s*', '', inpp) #去掉空白字符 149 150 # 表达式保存在列表中 151 result = exec_bracket(inpp) 152 print(result) 153 154 155 156 157 # ******************** 请计算表达式: 1 - 2 * ( (60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) ) ******************** 158 # before: 1-2*((60-30+(-40.0/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2)) 159 # -40.0/5=-8.0 160 # after: 1-2*((60-30+-8.0*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2)) 161 # ========== 上一次计算结束 ========== 162 # before: 1-2*((60-30+-8.0*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2)) 163 # 9-2*5/3+7/3*99/4*2998+10*568/14=173545.88095238098 164 # after: 1-2*((60-30+-8.0*173545.88095238098)-(-4*3)/(16-3*2)) 165 # ========== 上一次计算结束 ========== 166 # before: 1-2*((60-30+-8.0*173545.88095238098)-(-4*3)/(16-3*2)) 167 # 60-30+-8.0*173545.88095238098=-1388337.0476190478 168 # after: 1-2*(-1388337.0476190478-(-4*3)/(16-3*2)) 169 # ========== 上一次计算结束 ========== 170 # before: 1-2*(-1388337.0476190478-(-4*3)/(16-3*2)) 171 # -4*3=-12.0 172 # after: 1-2*(-1388337.0476190478--12.0/(16-3*2)) 173 # ========== 上一次计算结束 ========== 174 # before: 1-2*(-1388337.0476190478--12.0/(16-3*2)) 175 # 16-3*2=10.0 176 # after: 1-2*(-1388337.0476190478--12.0/10.0) 177 # ========== 上一次计算结束 ========== 178 # before: 1-2*(-1388337.0476190478--12.0/10.0) 179 # -1388337.0476190478--12.0/10.0=-1388335.8476190479 180 # after: 1-2*-1388335.8476190479 181 # ========== 上一次计算结束 ========== 182 # 2776672.6952380957