一、題目描述
請用 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
<