二、软件工程慕课第一章作业题——编写一个计算器
课程地址:https://next.xuetangx.com/learn/THU08091000367/THU08091000367/1516221/exercise/138588
一、题目描述
请用Python3编写一个计算器的控制台程序,支持加减乘除、乘方、括号、小数点,运算符优先级为括号>乘方>乘除>加减,同级别运算按照从左向右的顺序计算。
二、输入描述
-
数字包括"0123456789",小数点为".",运算符包括:加("+")、减("-")、乘("*")、除("/")、乘方("^",注:不是**!)、括号("()")
-
需要从命令行参数读入输入,例如提交文件为main.py,可以用python3 main.py "1+2-3+4"的方式进行调用,Java程序也是类似的,如果你的程序需要通过键盘输入,那么是不符合要求的,例如python使用input()来等待用户输入,这会因为自动评测时不会有用户输入所以不会有任何结果。
-
输入需要支持空格,即 python3 main.py "1 + 2 - 3 + 4" 也需要程序能够正确给出结果,Java程序也是类似的
-
所有测试用例中参与运算的非零运算数的绝对值范围保证在 10^9-10^(-10) 之内, 应该输出运算结果时非零运算结果绝对值也保证在该范围内
三、输出描述
-
数字需要支持小数点,输出结果取10位有效数字,有效数字位数不足时不能补0
-
对于不在输入描述内的输入,输出INPUT ERROR
-
对于格式不合法(例如括号不匹配等)的输入,输出 FORMAT ERROR
-
对于不符合运算符接收的参数范围(例如除0等)的输入,输出VALUE ERROR
-
对于2、3、4的情况,输出即可,不能抛出异常
-
同时满足2、3、4中多个条件时,以序号小的为准
四、样例
输入: 1 + 2 - 3 + 4
输出: 4
输入: 1 + 2 - 3 + 1 / 3
输出: 0.3333333333
输入: 1 + + 2
输出: FORMAT ERROR
输入: 1 / 0
输出: VALUE ERROR
输入: a + 1
输出: INPUT ERROR
五、思路总结
python中有一个eval函数可以直接进行算式的计算,但我估计这个作业的目的是让我们自己实现,故先对式子进行处理,式子的处理查阅了网上的一些方法,觉得自己能理解和写出来的就是利用正则表达式将字符串形式的算式变成列表,然后进行处理
1 import re 2 3 def conversionFormula(formula): 4 5 format_list = re.findall('[\w\.]+|\(|\+|\-|\*|\/|\^|\)',formula) 6 7 return format_list
这里要用到re库的方法对接收的字符串进行列表的转换,例如接受[1+2*3],转换后为[ '1', '+', '2', '*', '3' ],转换完成后就是对式子进行分析,最重要的就是对括号的处理,我采取的方法是对列表进行遍历,找到最内层的括号,然后对里面的式子进行求解,然后将此括号及中间的算式替换成答案,然后递归进行新式子的求解。下面贴出的方法可以将将列表里的括号及括号里的式子处理完毕后进行返回,格式依然是列表
1 def remove_bracket(formula): 2 leftBracket = 0 3 count = 0 #用遍历列表进行计数 4 5 for i in formula: 6 if i == '(': #记录最内侧左括号的位置 7 8 leftBracket=count 9 10 elif i == ')': 11 12 smallestFomula = formula[leftBracket+1:count] #提取最内层括号里式子 13 smallestFomulaAnswer = calculator(smallestFomula) #调用calculator方法进行计算 14 15 if smallestFomulaAnswer < 0: #更新式子,对去括号后的+-,--情况进行分析 16 if formula[leftBracket-1] == '-': 17 formula[leftBracket-1] = '+' 18 temp = formula[:leftBracket] 19 temp.append(str(abs(smallestFomulaAnswer))) 20 formula = temp+formula[count+1:] 21 22 elif formula[leftBracket-1] == '+': 23 formula[leftBracket-1] = '-' 24 temp = formula[:leftBracket] 25 temp.append(str(abs(smallestFomulaAnswer))) 26 formula = temp+formula[count+1:] 27 28 else: 29 temp = formula[:leftBracket] 30 temp.append(str(smallestFomulaAnswer)) 31 formula = temp+formula[count+1:] 32 33 else: 34 temp = formula[:leftBracket] 35 temp.append(str(smallestFomulaAnswer)) 36 formula = temp+formula[count+1:] 37 38 return remove_bracket(formula) #递归,进行新式子的去括号分析 39 40 count+=1 41 42 return formula #返回无括号的最终式子
还有一部分就是对括号内的式子进行计算
1 def calculator(formula): 2 count = 0 3 for i in formula: #先处理乘方、乘除 4 if i == '^': 5 formula[count-1]=str(float(formula[count-1])**float(formula[count+1])) 6 del(formula[count]) 7 del(formula[count]) 8 return calculator(formula) 9 10 elif i == '*': 11 formula[count-1]=str(float(formula[count-1])*float(formula[count+1])) 12 del(formula[count]) 13 del(formula[count]) 14 return calculator(formula) 15 16 elif i == '/': 17 formula[count-1]=str(float(formula[count-1])/float(formula[count+1])) 18 del(formula[count]) 19 del(formula[count]) 20 return calculator(formula) 21 22 count+=1 23 24 count=0 25 26 if formula[0]=='-': #处理第一个字符是’-’的情况 27 formula[1]=formula[0]+formula[1] 28 del(formula[0]) 29 30 for i in formula: #处理加减 31 if i == '+': 32 formula[count-1]=str(float(formula[count-1])+float(formula[count+1])) 33 del(formula[count]) 34 del(formula[count]) 35 return calculator(formula) 36 37 elif i == '-': 38 formula[count-1]=str(float(formula[count-1])-float(formula[count+1])) 39 del(formula[count]) 40 del(formula[count]) 41 return calculator(formula) 42 43 count+=1 44 45 return float(formula[0]) 返回float型的运算结果
最后是在主函数里对上述两个方法进行调用
1 if __name__ == '__main__': 2 3 formula=input('请输入算式:') 4 formula="".join(formula.split()) 5 formula = eq_format(formula) 6 formula = remove_bracket(formula) 7 answer = calculator(formula) 8 print(answer)
六、完善
到上述部分,大体功能就完成了,下面进行一些修修补补,要求里需要对空格、格式不对、补0等问题进行处理主要修改的有conversionFormula和主函数一些地方,下面贴出代码
1 def conversionFormula(formula): 2 3 format_list = re.findall('[\w\.]+|\(|\+|\-|\*|\/|\^|\)',formula) 4 switch =0 5 switch2=0 6 count=0 7 for i in format_list: 8 if i == '(' or i == ')': #进行括号符号的判断 9 switch2+=1 10 11 for i in format_list: 12 if i == '+' or i == '-' or i == '*' or i == '/' or i == '^':#进行加减乘除乘方符号的判断
13 switch =1 14 break 15 if switch != 1 : 16 print('INPUT ERROR') 17 return 'ERROR' 18 for i in format_list: #进行一些错误的判断 19 if i == '+' or i == '-' or i == '*' or i == '/' : 20 if format_list[count-1] == '+' or format_list[count-1] == '-' or\ 21 format_list[count-1] == '*' or format_list[count-1] == '/' or\ 22 format_list[count+1] == '+' or format_list[count+1] == '-' or\ 23 format_list[count+1] == '*' or format_list[count+1] == '/' : 24 print('FORMAT ERROR') 25 return 'ERROR' 26 elif i == '/' and format_list[count+1] == '0': 27 28 print('VALUE ERROR') 29 return 'ERROR' 30 elif i.isalpha() == True: 31 print('INPUT ERROR') 32 return 'ERROR' 33 count+=1 34 if switch2 % 2 != 0: 35 print('FORMAT ERROR') 36 return 'ERROR' 37 38 return format_list
1 if __name__ == '__main__': 2 3 formula=input('输入:') 4 formula="".join(shizi.split()) #空格处理 5 formula = conversionFormula(formula) 6 7 if formula != 'ERROR': #错误判断 8 formula = remove_bracket(formula) 9 answer = calculator(formula) 10 if float(answer).is_integer() == True: 11 answer=int(answer) 12 else: 13 answer=format(answer,'.10f') #空格补0,不足位数不补0 14 answer=float(str(answer).rstrip('0')) 15 print(answer) 16 else: 17 a=1
到此还有一个从命令行输入参数的要求,故将主函数中输入参数部分作修改
1 if __name__ == '__main__': 2 3 formula="".join(sys.argv[1:]) 4 formula = conversionFormula(formula) 5 6 if formula != 'ERROR': #错误判断 7 formula = remove_bracket(formula) 8 answer = calculator(formula) 9 if float(answer).is_integer() == True: 10 answer=int(answer) 11 else: 12 answer=format(answer,'.10f') #空格补0,不足位数不补0 13 answer=float(str(answer).rstrip('0')) 14 print(answer) 15 else: 16 a=1
这里在调试的时候遇到一个问题,题目要求乘方按照2^2的形式输入,我在dos中输入时报错:
![](https://img2018.cnblogs.com/common/1944951/202002/1944951-20200222183848304-335944317.png)
![](https://img2018.cnblogs.com/common/1944951/202002/1944951-20200222184050331-656535859.png)
或者”2^2"
![](https://img2018.cnblogs.com/common/1944951/202002/1944951-20200222184156088-508522855.png)
1 import re 2 import sys 3 4 5 def conversionFormula(formula): 6 7 format_list = re.findall('[\w\.]+|\(|\+|\-|\*|\/|\^|\)',formula) 8 switch =0 9 switch2=0 10 count=0 11 for i in format_list: 12 if i == '(' or i == ')': 13 switch2+=1 14 15 for i in format_list: 16 if i == '+' or i == '-' or i == '*' or i == '/' or i == '^': 17 switch =1 18 break 19 if switch != 1 : 20 print('INPUT ERROR') 21 return 'ERROR' 22 for i in format_list: 23 if i == '+' or i == '-' or i == '*' or i == '/' : 24 if format_list[count-1] == '+' or format_list[count-1] == '-' or\ 25 format_list[count-1] == '*' or format_list[count-1] == '/' or\ 26 format_list[count+1] == '+' or format_list[count+1] == '-' or\ 27 format_list[count+1] == '*' or format_list[count+1] == '/' : 28 print('FORMAT ERROR') 29 return 'ERROR' 30 elif i == '/' and format_list[count+1] == '0': 31 32 print('VALUE ERROR') 33 return 'ERROR' 34 elif i.isalpha() == True: 35 print('INPUT ERROR') 36 return 'ERROR' 37 count+=1 38 if switch2 % 2 != 0: 39 print('FORMAT ERROR') 40 return 'ERROR' 41 42 return format_list 43 44 45 def calculator(formula): 46 count = 0 47 for i in formula: 48 49 if i == '^': 50 formula[count-1]=str(float(formula[count-1])**float(formula[count+1])) 51 del(formula[count]) 52 del(formula[count]) 53 return calculator(formula) 54 55 if i == '*': 56 formula[count-1]=str(float(formula[count-1])*float(formula[count+1])) 57 del(formula[count]) 58 del(formula[count]) 59 return calculator(formula) 60 61 elif i == '/': 62 formula[count-1]=str(float(formula[count-1])/float(formula[count+1])) 63 del(formula[count]) 64 del(formula[count]) 65 return calculator(formula) 66 67 count+=1 68 69 count=0 70 71 if formula[0]=='-': 72 formula[1]=formula[0]+formula[1] 73 del(formula[0]) 74 75 for i in formula: 76 77 if i == '+': 78 formula[count-1]=str(float(formula[count-1])+float(formula[count+1])) 79 del(formula[count]) 80 del(formula[count]) 81 return calculator(formula) 82 83 elif i == '-': 84 formula[count-1]=str(float(formula[count-1])-float(formula[count+1])) 85 del(formula[count]) 86 del(formula[count]) 87 return calculator(formula) 88 89 count+=1 90 91 return float(formula[0]) 92 93 def remove_bracket(formula): 94 leftBracket = 0 95 count = 0 #用遍历列表进行计数 96 97 for i in formula: 98 if i == '(': 99 100 leftBracket=count 101 elif i == ')': 102 103 smallestFomula = formula[leftBracket+1:count] 104 smallestFomulaAnswer = calculator(smallestFomula) 105 if smallestFomulaAnswer < 0: 106 if formula[leftBracket-1] == '-': 107 formula[leftBracket-1] = '+' 108 temp = formula[:leftBracket] 109 temp.append(str(abs(smallestFomulaAnswer))) 110 formula = temp+formula[count+1:] 111 112 elif formula[leftBracket-1] == '+': 113 formula[leftBracket-1] = '-' 114 temp = formula[:leftBracket] 115 temp.append(str(abs(smallestFomulaAnswer))) 116 formula = temp+formula[count+1:] 117 118 else: 119 temp = formula[:leftBracket] 120 temp.append(str(smallestFomulaAnswer)) 121 formula = temp+formula[count+1:] 122 else: 123 temp = formula[:leftBracket] 124 temp.append(str(smallestFomulaAnswer)) 125 formula = temp+formula[count+1:] 126 127 return remove_bracket(formula) 128 count+=1 129 130 return formula 131 132 if __name__ == '__main__': 133 134 formula="".join(sys.argv[1:]) 135 formula = conversionFormula(formula) 136 137 if formula != 'ERROR': 138 formula = remove_bracket(formula) 139 answer = calculator(formula) 140 if float(answer).is_integer() == True: 141 answer=int(answer) 142 else: 143 answer=format(answer,'.10f') 144 answer=float(str(answer).rstrip('0')) 145 print(answer) 146 else: 147 a=1
程序运行如下