day4 計算機,day4
作業:計算機開發
(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) )等類似公式後,必須自己解析裡面的(),+,-,*,/符號和公式,運算後得出結果,結果必須與真實的計算機所得出的結果一致。
代碼如下:
1 import re 2 3 formula = '1 - 2 * ( (60-30 +(-9-2*5/-3 + 7 /3*99/4*2998 +10 * 568/14 ) * (-40/5)) - (-4*3)/ (16-3*2) )' 4 #formula = "(1++1)" 5 def modify(formula_deep): 6 '''程式修飾函數,去除空格和括弧''' 7 '''去除運算中出現的+- -- ++ -+ 等情形''' 8 formula_deep = re.sub("[() ]","",formula_deep) #替換空格和空號 9 formula_deep = re.sub("\+-","-",formula_deep) #替換+-為-10 formula_deep = re.sub("--",'+',formula_deep) #替換--為+11 formula_deep = re.sub("-\+",'-',formula_deep)12 formula_deep = re.sub("\++","+",formula_deep)13 return formula_deep14 15 def multiply_divide(formula_deep):16 '''計算乘除'''17 '''由於乘除是首先計算的,我們的思路是,首先計算乘除,然後把計算的結果替換進去,就可以得到只剩加減的情形'''18 calc_sign = re.findall("[+-]",formula_deep) #提取字串中所有的加減號19 calc_list = re.split("[+-]",formula_deep) #以加減號進行分割,得到乘除20 '''由於我們得到的calc_list:['', '9', '2*5/', '3', '7/3*99/4*2998', '10*568/14'],裡面由於-號引起的麻煩,-9被分割了,2*5/等'''21 if calc_list[0] == '':22 '''處理列表開頭“”空的情況,說明這裡是負數,被我們分割掉了要重新進行合并'''23 calc_list[1] = calc_sign[0] + calc_list[1]24 del calc_sign[0]25 del calc_list[0]26 for num,line in enumerate(calc_list):27 '''處理2*5/的情形,說明這種後面除的是一個負數,因為只有負數才會出現這種情況2*5/-3被分割了,需要合并'''28 if line.endswith("/") or line.endswith("*"):29 '''如果結尾包括乘除號,說明是負數被拆分了'''30 calc_list[num+1] = calc_list[num] + calc_sign[num] + calc_list[num+1]31 del calc_sign[num]32 del calc_list[num]33 '''下面進行乘除的正式運算,上面都是進行格式轉換'''34 for index,string in enumerate(calc_list):35 '''首先提取編號,便於後面替換運算出來的值'''36 if "/" in string or "*" in string:37 mul_div_sign = re.findall("[/*]",string)38 mul_div_list = re.split("[/*]",string)39 calc_value = None40 for e_index,e in enumerate(mul_div_list):41 if calc_value:42 if mul_div_sign[e_index-1] == "/":43 calc_value /= float(e)44 elif mul_div_sign[e_index-1] == "*":45 calc_value *= float(e)46 else:47 calc_value = float(e)48 calc_list[index] = calc_value49 else:50 pass51 '''計算值'''52 value = None53 for k,v in enumerate(calc_list):54 '''計算加減的情況'''55 if value:56 if calc_sign[k-1] == "-":57 value -= float(v)58 elif calc_sign[k-1] == '+':59 value += float(v)60 else:61 value = float(v)62 return value63 64 65 def main(formula):66 '''程式主入口,產生帶括弧的情況'''67 while True:68 formula_deep = re.search("\(.[^()]+\)",formula)69 if formula_deep:70 formula_deep = formula_deep.group()71 formula_list = modify(formula_deep)72 '''得到修整好要計算的字串,現在開始進行計算-9-2*5/-3+7/3*99/4*2998+10*568/14'''73 calc_value = multiply_divide(formula_list)74 formula = formula.replace(formula_deep,str(calc_value))75 else:76 '''處理不帶括弧的情形'''77 formula = modify(formula)78 calc_last_value = multiply_divide(formula)79 print("formula:",calc_last_value)80 exit()81 82 if __name__=="__main__":83 main(formula)
程式運行過程:
大致思路:我們知道,要計算上面字串的格式,可以使用eval()函數,但是這裡我們要自己編寫一個計算機;我們知道,數學運算的優先順序是括弧的優先順序最高,先運算括弧內的東西,因此我們的思路是匹配出記憶體的括弧,然後進行運算,當把記憶體括弧中的內容匹配出來之後,我們計算,然後用計算出來的值替換字串中原來位置的值,一直替換到字串中沒有括弧位置,這個時候我們就按照正常的運算順序來進行計算。
1、正則匹配,先找到記憶體括弧;代碼如下:
formula = '1 - 2 * ( (60-30 +(-9-2*5/-3 + 7 /3*99/4*2998 +10 * 568/14 ) * (-40/5)) - (-4*3)/ (16-3*2) )'
formula_deep = re.search("\(.[^()]+\)",formula)
print(formula_deep.group())
運行結果如下:
(-9-2*5/-3 + 7 /3*99/4*2998 +10 * 568/14 )
上面我們觀察提取出來記憶體的字串,可以看出,有很多地方需要修飾一下,字串中有很多空,這會影響我們計算,還有在計算的過程中我們不需要括弧,因此也要去掉括弧。
2、去除空格和括弧;
formula_deep = re.sub("[() ]","",formula_deep)
運行如下:
-9-2*5/-3+7/3*99/4*2998+10*568/14
3、得到上述字串之後,我們在逐層運算的時候,可以會出現+-,-+,++,--等情況,這個時候也要進行處理,由於現在是第一層,看不出來問題,當運算之後,如果記憶體得到的是負數,那麼前面括弧外面是+或-的話就變成+-,--等情況,這個時候也是不行的,也要進行處理;
formula_deep = re.sub("\+-",'-',formula_deep)
formula_deep = re.sub("-+",'-',formula_deep)
formula_deep = re.sub("\++","+",formula_deep)
formula_deep = re.sub("--",'+',formula_deep)
運行如下:
-9-2*5/-3+7/3*99/4*2998+10*568/14
4、當上面處理完成之後,就正式開始計算,我們知道,計算首先要計算乘除,因為乘除的優先順序最高,因為要先找到乘除,計算完成之後再計算加減;
calc_sign = re.findall("[+-]",formula_deep)
calc_list = re.split("[+-]",formula_deep)
print(calc_list)
print(calc_sign)
運行如下:
['', '9', '2*5/', '3', '7/3*99/4*2998', '10*568/14']
['-', '-', '-', '+', '+']
我們得到了計算的列表,和運算子號,['', '9', '2*5/', '3', '7/3*99/4*2998', '10*568/14']裡面也是有問題的,首先,列表的第一個元素是""空的情形,說明前面是-號才會有這種情況出現,這樣的情況是要合并的,還有2*5/,這種說明後面跟著的也是負數,也要先進行處理才能進行預案算,為什麼會出現這種情況呢?這是由於在運算的過程中,我們是以"+"或"-"來分割字串的,這樣就會造成如果是一個負數,就會被分割掉,在開頭的時候,前面會被分割一個空,這樣就會造成我們運算出錯,因為要進行修改。
5、對上面運算進行修正:
if calc_list[0] == "":
calc_list[1] = calc_sign[0] + calc_list[1]
del calc_list[0]
del calc_sign[0]
print("calc_list:",calc_list)
print("calc_sign:",calc_sign)
for index,e in enumerate(calc_list):
if e.endswith("/") or e.endswith("*"):
'''說明後面跟著的是負數,要進行修正'''
calc_list[index+1] = calc_list[index] + calc_sign[index] + calc_list[index+1]
del calc_list[index]
del calc_sign[index]
print("calc_list:",calc_list)
print("calc_sign:",calc_sign)
運行如下:
calc_list: ['-9', '2*5/', '3', '7/3*99/4*2998', '10*568/14']
calc_sign: ['-', '-', '+', '+']
calc_list: ['-9', '2*5/-3', '7/3*99/4*2998', '10*568/14']
calc_sign: ['-', '+', '+']
從上面可以看出,我們分兩筆進行了修正,第一次去除了開頭的""空元素問題;第二次提出了乘除後面跟著負數的情況;
6、這個時候,我們就要進行計算了,我們首先遍曆calc_list: ['-9', '2*5/-3', '7/3*99/4*2998', '10*568/14']中的元素,因為我們要先進行乘除,因此找到包含"/"或"*"的字串,進行求值,然後進行替換,就可以得到沒有乘除的字串,只包含加減情況:
for num,value in enumerate(calc_list): if "/" in value or "*" in value: """說明包含乘除,首先進行計算""" mul_div_sign = re.findall("[/*]",value) mul_div_list = re.split("[*/]",value) print(mul_div_sign) print(mul_div_list)
運算結果如下:
['*', '/']
['2', '5', '-3']
['/', '*', '/', '*']
['7', '3', '99', '4', '2998']
['*', '/']
['10', '568', '14']
我們得到了運算子和裡面的數字,現在只要判斷乘除號,然後就可以利用前面一個乘以後面一個進行計算了。如下:
7、乘除計算:
1 for num,value in enumerate(calc_list): 2 if "/" in value or "*" in value: 3 """說明包含乘除,首先進行計算""" 4 mul_div_sign = re.findall("[/*]",value) 5 mul_div_list = re.split("[*/]",value) 6 '''接下來,我們計算乘除的情況,首先我們要遍曆乘除,因為要進行元素遞乘''' 7 res = None 8 for e_index,e_value in enumerate(mul_div_list): 9 if res:10 if mul_div_sign[e_index-1] == "/":11 res /= float(e_value)12 elif mul_div_sign[e_index-1] == "*":13 res *= float(e_value)14 else:15 res = float(e_value) #如果不存在,就產生一個新的,但是我們定義的是不存在的情況,因此肯定是先產生一個數,然後在進行計算16 calc_list[num] = res17 else:18 pass19 20 print(calc_list)21 print(calc_sign)
運行結果如下:
['-9', -3.3333333333333335, 173134.50000000003, 405.7142857142857]
['-', '+', '+']
上述代碼,我們進行了乘除的運算,讓運算裡面不在存在乘除,只需要進行加減運算即可。
可以看見,我們運算之後,只剩下了加減,這樣,我們就可以利用列表的元素和符號進行加減運算。
8、加減運算:
1 '''進行加減運算''' 2 result = None 3 for k_index,k_value in enumerate(calc_list): 4 if result: 5 if calc_sign[k_index-1] == "+": 6 result += float(k_value) 7 elif calc_sign[k_index-1] == '-': 8 result -= float(k_value) 9 else:10 result = float(k_value)11 print("result:",result)
運行如下:
result: 173534.54761904766
9、上面,我們得到了運算的結果,然後只需要替換記憶體括弧的內容即可,這樣一層一層替換,最終只會剩下沒有括弧的運算,這個時候,我們在這行這個函數,就能得到最終的結果。
知識點:
(1):
result = None
for k_index,k_value in enumerate(calc_list):
if result:
if calc_sign[k_index-1] == "+":
result += float(k_value)
elif calc_sign[k_index-1] == '-':
result -= float(k_value)
else:
result = float(k_value)
上述代碼中,體現了一個思想,由於我們想實現的是前一個數字加上後一個數字,但是沒有直接的方法,這個時候,我們就可以先定義一個空值,然後對這個值進行判斷,其實判斷的目的就是為了給這個變數賦值,賦值就是列表的第一個元素,這樣我們就能實現列表中元素每次迴圈都進行疊加或疊減。這個思想很好。當不存在,想讓它存在的時候,就先定義一個空值進行判斷,判斷之後在進行賦值。賦值之後,相當於result等於元素的第一個值,並且元素下一次迴圈也是從第二個值開始,列表的長度索引也沒有超標。
2、Regex的利用,re(regular的縮寫):
^表示非,"\"表示轉義,就是讓表示字元本身的含義,+代表一個或多個
"\(.[^()]+\)"代表匹配括弧,括弧中間是包含任意多個不是()的元素,也就是匹配最內層的括弧。
3、字串的replace()方法,就把字串進行尋找替換,str.replace(old,new),正則中字串findall()尋找元素中所有的Regex的值,放在一個列表中;re.split()字串的分割,按照某個正則字串進行分割。