一、题目描述
请用 python3编写一个计算器的控制台程序,支持加减乘除、乘方、括号、小数点,运算符优先级为括号>乘方>乘除>加减,同级别运算按照从左向右的顺序计算。
二、输入描述
数字包括"0123456789",小数点为".",运算符包括:加("+")、减("-")、乘("*")、除("/")、乘方("^",注:不是**!)、括号("()")
需要从命令行参数读入输入,例如提交文件为 main.py,可以用 python3 main.py "1+2-3+4" 的方式进行调用
输入需要支持空格,即 python3 main.py "1 + 2 - 3 + 4" 也需要程序能够正确给出结果
所有测试用例中参与运算的非零运算数的绝对值范围保证在 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
【注:此题为TsinghuaX:34100325X 《软件工程》 MOOC 课程 Spring, 2016 Chapter 1 Problem,此文发布时,这门课刚刚在 “学堂在线” 上开课两天】
用 Python3 实现,初看一下,首先想到的其实是一种“讨巧”(作弊 >_<)的方法(由于曾经网站被挂码的悲壮历史……),即通过 eval() 函数(这应该是黑客用得最多的一个函数了吧+_+)直接求解表达式,谁叫题目指定用 Python 这种方便的脚本语言呢~
大致代码区区几行:
1 from sys importargv2
3 if __name__ == "__main__":4 exp = argv[1]5 print(eval(exp))
即便是考虑到题干中的输出要求做异常处理(try...except)输出相应的错误信息,也就十行左右。
但稍深入就会发现,有些情形还是无法按要求处理的,比如样例 “1 + + 2”,用 eval() 会输出结果 “3” (+2 前的 “+” 被当作正号),而题目要求输出 “FORMAT ERROR”。
没办法,只能老老实实做苦力活儿了。
表达式求值其实是《数据结构》课程里一个基本且重要的问题之一,一般作为 “栈” 的应用来提出。
问题的关键就是需要按照人们通常理解的运算符的优先级来进行计算,而在计算过程中的临时结果则用 栈 来存储。
为此,我们可以首先构造一个 “表” 来存储当不同的运算符 “相遇” 时,它们谁更 “屌” 一些(优先级更高一些)。这样就可以告诉计算机,面对不同的情形,它接下来应该如何来处理。
其次,我们需要构造两个栈,一个运算符栈,一个运算数栈。
运算符栈是为了搞定当某个运算符优先级较低时,暂时先让它呆在栈的底部位置,待它可以 “重见天日” 的那一天(优先级相对较高时),再把它拿出来使用。正确计算完成后,此栈应为空。
运算数栈则是为了按合理的计算顺序存储运算中间结果。正确计算完成后,此栈应只剩下一个数,即为最后的结果。
完整的代码如下:
1 #-*- coding: utf-8 -*-
2
3 #################################
4 #@Author: Maples7
5 #@LaunchTime: 2016/2/24 12:32:38
6 #@FileName: main
7 #@Email: maples7@163.com
8 #@Function:
9 #
10 #A Python Calculator for Operator +-*/()^
11 #12 #################################
13
14 from sys importargv15 from decimal import *
16
17 defdelBlank(str):18 """
19 Delete all blanks in the str20 """
21 ans = ""
22 for e instr:23 if e != " ":24 ans +=e25 returnans26
27 defprecede(a, b):28 """
29 Compare the prior of operator a and b30 """
31 #the prior of operator
32 prior =(33 #'+' '-' '*' '/' '(' ')' '^' '#'
34 ('>', '>', '<', '<', '<', '>', '<', '>'), #'+'
35 ('>', '>', '<', '<', '<', '>', '<', '>'), #'-'
36 ('>', '>', '>', '>', '<', '>', '<', '>'), #'*'
37 ('>', '>', '>', '>', '<', '>', '<', '>'), #'/'
38 ('<', '<', '<', '<', '<', '=', '<', ' '), #'('
39 ('>', '>', '>', '>', ' ', '>', '>', '>'), #')'
40 ('>', '>', '>', '>', '<', '>', '>', '>'), #'^'
41 ('<', '<', '<', '<', '<', ' ', '<', '=') #'#'
42 )43
44 #operator to index of prior[8][8]
45 char2num ={46 '+': 0,47 '-': 1,48 '*': 2,49 '/': 3,50 '(': 4,51 ')': 5,52 '^': 6,53 '#': 7
54 }55
56 returnprior[char2num[a]][char2num[b]]57
58 defoperate(a, b, operator):59 """
60 Operate [a operator b]61 """
62 if operator == '+':63 ans = a +b64 elif operator == '-':65 ans = a -b66 elif operator == '*':67 ans = a *b68 elif operator == '/':69 if b ==0:70 ans = "VALUE ERROR"
71 else:72 ans = a /b73 elif operator == '^':74 if a == 0 and b ==0:75 ans = "VALUE ERROR"
76 else:77 ans = a **b78
79 returnans80
81 defcalc(exp):82 """
83 Calculate the ans of exp84 """
85 exp += '#'
86 operSet = "+-*/^()#"
87 stackOfOperator, stackOfNum = ['#'], []88 pos, ans, index, length =0, 0, 0, len(exp)89 while index
<