Python之實現一個簡易計算器


 

自己動手寫計算器

一、功能分析

  用戶輸入一個類似這樣 3*( 4+ 50 )-(( 100 + 40 )*5/2- 3*2* 2/4+9)*((( 3 + 4)-4)-4) 這樣的表達式,假設表達式里面除了包含空格、'+'、'-'、'*'、'/'和括號再無其他特殊符號,然后自己動手寫代碼解析其中的表達式,實現加減乘除最后得出的結果與真實的計算機所算的結果必須一致。

二、所需的知識點

  • 字符串的處理
  • 正則表達式的運用
  • 函數遞歸

三、程序實現流程分析

  1. 用正則表達式處理字符串,只提取其中的數字和運算符,並轉換成列表
  2. 編寫一個函數,處理沒有括號的基本運算的基本表達式
  3. 再寫一個函數遞歸處理帶有括號的函數,先計算最內部括號中的表達式, 然后將最內部的括號替換為計算后的結果, 在遞歸外部一層的, 最后返回的就是所需的結果

四、具體實現過程

1.正則表達式處理用戶輸入字符串

  這里我不會講正則表達式具體的用法,要將的話都可以講一本書了,我只講本文用到的正則表達式。根據需求,我們需要提取出用戶輸入字符串中的數字和運算符到一個列表中,而空格將會被忽略掉,假設用戶輸入的表達式是 expression,我們可以寫出下面的代碼:

1
2
3
4
import  re
expression = '(( 100 + 40 )*5/2- 3*2* 2/4+9)*((( 3 + 4)-4)-4)'
l = re.findall( '([\d\.]+|/|-|\+|\*)' ,expression)
print (l)  #['100', '+', '40', '*', '5', '/', '2', '-', '3', '*', '2', '*', '2', '/', '4', '+', '9', '*', '3', '+', '4', '-', '4', '-', '4'] 

首先我們先看一下 findall 的用法,findall可以匹配所有符合規律的內容,返回包含結果的列表。'([\d\.]+|/|-|\+|\*)'是匹配規則,這里\d表示匹配一個數字,\.表示將.轉義成數字上小數點 . ,不然在正則表達式里 . 可以匹配除了換行符以外的任意字符。[\d\.]+表示可以匹配至少由一個數字、或者小數點 . 組成的字符串,比如說,這里既可以匹配到100,也可以匹配到100.11。|/|-|\+|\* 表示匹配到+或-或*或/,()表示一組,這里意思是如果匹配到數字或者+或者-或者*或者/其中任意一個的話,就將其作為一組,然后添加到列表中去。

2.不含括號的表達式的計算

  為了后面迭代算出有括號的表達式,我們先寫一個沒有括號的表達式,比如說像這樣一個表達式 '100.5+40*5/2-3*2*2/4+9',對於這樣的表達式我們肯定是計算乘除,在計算加減,計算一個最小計算單元后,再將結果放回列表中不斷循環,直到算出整個不帶括號的表達式,實現的代碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import  re
expression =  '100.5+40*5/2-3*2*2/4+9'
=  re.findall( '([\d\.]+|/|-|\+|\*)' ,expression)
print ( 100.5 + 40 * 5 / 2 - 3 * 2 * 2 / 4 + 9 )                      # 206.5
def  multdiv(l,x):                                  #定義最小的乘除運算單元,l是列表,x代表*或/
     =  l.index(x)                                 #首先獲取乘除運算符的位置
     if  x = = '*' :                                     #如果是*則執行乘法運算
         =  float (l[a  -  1 ])  *  float (l[a  +  1 ])      #獲取乘法運算的結果,比如k=3*2
     else :
         =  float (l[a  -  1 ])  /  float (l[a  +  1 ])
     del  l[a  -  1 ], l[a  -  1 ], l[a  -  1 ]               #刪除掉列表里剛做運算的三個元素,比如,3 * 2
     l.insert(a  -  1 str (k))                        #將剛計算的結果插入到列表中然后執行下一次計算
     return  l
 
def  fun(s):
     sum = 0
     while  1 :                                      #先將乘除運算計算完,在計算加減
         if  '*'  in  and  '/'  not  in  l:             #先判斷,如果只有*的話,先計算 *
             multdiv(l,  '*' )
         elif  '*'  not  in  and  '/'  in  l:           #如果只有 /的話,先計算 /
             multdiv(l,  '/' )
         elif  '*'  in  and  '/'  in  l:               #如果既有 / 也有 *的話,先獲取他們的下標,
             =  l.index( '*' )                      #根據下標判斷先執行哪個
             =  l.index( '/' )
             if  a < b:
                 multdiv(l,  '*' )
             else :
                 multdiv(l,  '/' )
         else :                                    #當上面的乘除計算完之后,就可以計算加減了
             if  l[ 0 ] = = '-' :                        #這里需要判斷一下,如果列表里第一個符號是‘-’
                 l[ 0 ] = l[ 0 ] + l[ 1 ]                   #的話,表示第一個數是負數,所以我們需要將列表第一和第二項合並起來
                 del  l[ 1 ]
             sum  + =  float (l[ 0 ])                   #做完上面的處理后列表中就只剩加減計算了,
             for  in  range ( 1 , len (l), 2 ):
                 if  l[i] = = '+' :                    #根據符號執行加減計算,將結果保存在sum中
                     sum + = float (l[i + 1 ])
                 else :
                     sum - = float (l[i + 1 ])
             break
     return   sum                                  #最后返回這個不含括號表達式的結果
a = fun(l)
print (a)                                         # 206.5 可以看出與實際的計算結果一樣

  代碼寫到這里主要的功能實現了,但是上面的代碼還有一個小問題,那就是如果我們的表達式如果是這樣的 7*((1-4)-4) 我們按照程序流程執行的話執行一次fun的話,表達式變成這樣 7*(-3-4),在執行一次的話就變成 7*-7,這樣的話,我們在執行上面的fun函數就會出現問題,因為兩個數字之間出現了兩個運算符,所以我們要修改上面的函數使其能處理這種情況。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def  multdiv(l,x):                                   #定義最小的乘除運算單元,l是列表,x代表*或/
     =  l.index(x)                                   #首先獲取乘除運算符的位置
     if  = =  '*'  and  l[a  +  1 ] ! =  '-' :                 #判斷*,/后面的一個操作符是否是‘-’如果是的話,分別進行處理
         =  float (l[a  -  1 ])  *  float (l[a  +  1 ])
     elif  = =  '/'  and  l[a  +  1 ] ! =  '-' :
         =  float (l[a  -  1 ])  /  float (l[a  +  1 ])
     elif  = =  '*'  and  l[a  +  1 = =  '-' :
         =  - ( float (l[a  -  1 ])  *  float (l[a  +  2 ]))
     elif  = =  '/'  and  l[a  +  1 = =  '-' :
         =  - ( float (l[a  -  1 ])  /  float (l[a  +  2 ]))
     del  l[a  -  1 ], l[a  -  1 ], l[a  -  1 ]                 #刪除掉列表里剛做運算的三個元素,比如,3 * 2
     l.insert(a  -  1 str (k))                          #將剛計算的結果插入到列表中然后執行下一次計算
     return  l
 
def  fun(l):
     sum = 0
     print (l)
     while  1 :                                      #先將乘除運算計算完,在計算加減
         if  '*'  in  and  '/'  not  in  l:             #先判斷,如果只有*的話,先計算 *
             multdiv(l,  '*' )
         elif  '*'  not  in  and  '/'  in  l:           #如果只有 /的話,先計算 /
             multdiv(l,  '/' )
         elif  '*'  in  and  '/'  in  l:               #如果既有 / 也有 *的話,先獲取他們的下標,
             =  l.index( '*' )                      #根據下標判斷先執行哪個
             =  l.index( '/' )
             if  a < b:
                 multdiv(l,  '*' )
             else :
                 multdiv(l,  '/' )
         else :                                               #當上面的乘除計算完之后,就可以計算加減了
             print (l)
             if  l[ 0 ] = = '-' :                                   #這里需要判斷一下,如果列表里第一個符號是‘-’
                 l[ 0 ] = l[ 0 ] + l[ 1 ]                          #的話,表示第一個數是負數,所以我們需要將列表第一和第二項合並起來
                 del  l[ 1 ]
             sum  + =  float (l[ 0 ])                          #做完上面的處理后列表中就只剩加減計算了,
             for  in  range ( 1 len (l),  2 ):
                 if  l[i]  = =  '+'  and  l[i  +  1 ] ! =  '-' :        #判斷+,-后面的一個操作符是否是‘-’如果是的話,分別進行處理
                     sum  + =  float (l[i  +  1 ])
                 elif  l[i]  = =  '+'  and  l[i  +  1 = =  '-' :
                     sum  - =  float (l[i  +  2 ])
                 elif  l[i]  = =  '-'  and  l[i  +  1 = =  '-' :
                     sum  + =  float (l[i  +  2 ])
                 elif  l[i]  = =  '-'  and  l[i  +  1 ] ! =  '-' :
                     sum  - =  float (l[i  +  1 ])
             break
     return   sum                                          #最后返回這個不含括號表達式的結果

到這里,我們就完成了不含括號表達式的運算,程序的一大半就完成了,下面我們在完成剩下的程序。

3.帶有括號表達式的遞歸計算

首先計算最里面一個括號里的表達式,調用fun函數計算出其值,將其結果代替其括號,然后不停的遞歸調用直到獲取最后的結果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def  calculate(expression):
     ex = []                        #存儲'('出現的位置
     ans = 0                        #保存結果
     if  '('  not  in  expression:    #如果括號都處理完成了,直接調用fun函數返回結果
         ans = fun(expression)
         return  ans
     for  in  range ( len (expression)):
         if  expression[i] = = '(' :
             ex.append(i)  #ex=[6,7]                          #紀錄 '(' 出現的位置
         elif  expression[i] = = ')' :                             #遇到 ')'后。就可以計算第一個括號里的值
             temp = 0                                           #定義一個變量 存儲括號表達式的結果
             sub = expression[ex[ len (ex) - 1 ] + 1 :i]                #獲取括號里的表達式
             temp = fun(sub)                                    #調用fun函數計算括號里的表達式的值
             expression = expression[ 0 :ex[ len (ex) - 1 ]] + str (temp) + expression[i + 1 : len (expression) + 1 ]   #去掉剛才的括號表達式,並用temp代替,返回一個新的表達式
             ex.pop()                                         #刪除剛才計算完的括號表達式里面 '(' 的位置
             return  calculate(expression)                     #遞歸計算新的表達式,直道所有的括號處理完畢

4.大功告成

到這里所有的模塊都完成了,一個簡單的計算器就實現了,下面附上完整的代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import  re
def  md(l,x):
     =  l.index(x)
     if  = =  '*'  and  l[a  +  1 ] ! =  '-' :
         =  float (l[a  -  1 ])  *  float (l[a  +  1 ])
     elif  = =  '/'  and  l[a  +  1 ] ! =  '-' :
         =  float (l[a  -  1 ])  /  float (l[a  +  1 ])
     elif  = =  '*'  and  l[a  +  1 = =  '-' :
         =  - ( float (l[a  -  1 ])  *  float (l[a  +  2 ]))
     elif  = =  '/'  and  l[a  +  1 = =  '-' :
         =  - ( float (l[a  -  1 ])  /  float (l[a  +  2 ]))
     del  l[a  -  1 ], l[a  -  1 ], l[a  -  1 ]
     l.insert(a  -  1 str (k))
     return  l
 
def  fun(s):
     =  re.findall( '([\d\.]+|/|-|\+|\*)' ,s)
     sum = 0
     while  1 :
         if  '*'  in  and  '/'  not  in  l:
             md(l,  '*' )
         elif  '*'  not  in  and  '/'  in  l:
             md(l,  '/' )
         elif  '*'  in  and  '/'  in  l:
             =  l.index( '*' )
             =  l.index( '/' )
             if  a < b:
                 md(l,  '*' )
             else :
                 md(l,  '/' )
         else :
             if  l[ 0 ] = = '-' :
                 l[ 0 ] = l[ 0 ] + l[ 1 ]
                 del  l[ 1 ]
             sum  + =  float (l[ 0 ])
             for  in  range ( 1 len (l),  2 ):
                 if  l[i]  = =  '+'  and  l[i  +  1 ] ! =  '-' :
                     sum  + =  float (l[i  +  1 ])
                 elif  l[i]  = =  '+'  and  l[i  +  1 = =  '-' :
                     sum  - =  float (l[i  +  2 ])
                 elif  l[i]  = =  '-'  and  l[i  +  1 = =  '-' :
                     sum  + =  float (l[i  +  2 ])
                 elif  l[i]  = =  '-'  and  l[i  +  1 ] ! =  '-' :
                     sum  - =  float (l[i  +  1 ])
             break
     return  sum
def  calculate(expression):
     ex = []
     ans = 0
     if  '('  not  in  expression:
         ans = fun(expression)
         return  ans
     for  in  range ( len (expression)):
         if  expression[i] = = '(' :
             ex.append(i)  #ex=[6,7]
         elif  expression[i] = = ')' #14
             temp = 0
             sub = expression[ex[ len (ex) - 1 ] + 1 :i]
             temp = fun(sub)
             expression = expression[ 0 :ex[ len (ex) - 1 ]] + str (temp) + expression[i + 1 : len (expression) + 1 ]
             ex.pop()
             return  calculate(expression)
 
s = '1 - 2 * ( (60-30 +(-40/5+3) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )'
print ( 1  -  2  *  ( ( 60 - 30  + ( - 40 / 5 + 3 *  ( 9 - 2 * 5 / 3  +  7  / 3 * 99 / 4 * 2998  + 10  *  568 / 14  ))  -  ( - 4 * 3 ) /  ( 16 - 3 * 2 ) ))     #1735397.4095238098
s3 = '3*(4+50)-((100+40)*5/2-3*2*2/4+9)*(((3+4)-4)-4)'                                  #518.0
print ( 3 * ( 4 + 50 ) - (( 100 + 40 ) * 5 / 2 - 3 * 2 * 2 / 4 + 9 ) * ((( 3 + 4 ) - 4 ) - 4 ))
print (calculate(s))                                                    #1735397.4095238098
print (calculate(s3))                                                   #518.0

  為了簡潔性,上面完整的代碼沒有寫注釋,要看注釋的話可以往文章的上面去查看,最后為了可以簡單的對比計算器的正確性,就沒有加入input部分來獲取用戶的輸入,直接在代碼中用字符串代替了,代碼的最后可以看出代碼正確的運行了,到這里簡易計算器就完成了。

五、補充

  最近深入的學一下正則表達式,發現上面寫的計算器,比較復雜,所以就想用正則在經行改寫一下,下面是改寫后的代碼,改寫后去除注釋不到40行代碼,非常簡潔,下面來看一下代碼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import  re
def  multiply_divide(s):                          #計算一個不含括號的最小乘除單元,用split分隔*或/然后計算
     ret  =  float (s.split( '*' )[ 0 ])  *  float (s.split( '*' )[ 1 ])  if  '*'  in  else  float (s.split( '/' )[ 0 ])  /  float (
         s.split( '/' )[ 1 ])
     return  ret
 
def  remove_md(s):                                # 將不含括號的表達式里的乘除先遞歸計算完
     if  '*'  not  in  and  '/'  not  in  s:
         return  s                                 # 沒有乘除的話遞歸結束
     else :                                        # 匹配一個最小乘除單元,調用multiply_divide計算,將結果拼接成一個新的表達式進行遞歸處理
         =  re.search(r '-?[\d\.]+[*/]-?[\d\.]+' , s).group()
         =  s.replace(k,  '+'  +  str (multiply_divide(k)))  if  len (re.findall(r '-' , k))  = =  2  else  s.replace(k,  str (
             multiply_divide(k)))
         return  remove_md(s)
 
def  add_sub(s):                                   # 計算沒有乘除的表達式,得出最后不包含括號表達式的運算結果
     =  re.findall( '([\d\.]+|-|\+)' , s)           # 將表達式轉換成列表,
     if  l[ 0 = =  '-' :                               # 如果第一個數是負數,對其進行處理
         l[ 0 =  l[ 0 +  l[ 1 ]
         del  l[ 1 ]
     sum  =  float (l[ 0 ])
     for  in  range ( 1 len (l),  2 ):                 # 循環計算結果
         if  l[i]  = =  '+'  and  l[i  +  1 ] ! =  '-' :
             sum  + =  float (l[i  +  1 ])
         elif  l[i]  = =  '+'  and  l[i  +  1 = =  '-' :
             sum  - =  float (l[i  +  2 ])
         elif  l[i]  = =  '-'  and  l[i  +  1 = =  '-' :
             sum  + =  float (l[i  +  2 ])
         elif  l[i]  = =  '-'  and  l[i  +  1 ] ! =  '-' :
             sum  - =  float (l[i  +  1 ])
     return  sum
 
def  basic_operation(s):                            # 計算一個基本的4則運算
     =  s.replace( ' ' , '')
     return  add_sub(remove_md(s))                   # 調用前面定義的函數,先乘除,后加減
 
def  calculate(expression):                         # 計算包含括號的表達式
     if  not  re.search(r '\([^()]+\)' , expression):                     # 匹配最里面的括號,如果沒有的話,直接進行運算,得出結果
         return  basic_operation(expression)
     =  re.search(r '\([^()]+\)' , expression).group()                 # 將匹配到的括號里面的表達式交給basic_operation處理后重新拼接成字符串遞歸處理
     expression  =  expression.replace(k,  str (basic_operation(k[ 1 : len (k)  -  1 ])))
     return  calculate(expression)
 
=  '1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )'
print ( '用eval計算出來的值為:{}\n計算器計算出來的值為:{}' . format ( eval (s), calculate(s)))
# >>> 用eval計算出來的值為:2776672.6952380957
# >>> 計算器計算出來的值為:2776672.6952380957 

六、小結 

  看了上面的代碼,是不是覺自己寫代碼還是好麻煩啊,那么Python有沒有已經寫好的函數幫我們完成這一功能了,作為追求簡潔的python來說必須有,一行代碼解決上面我們做的所有事,而且功能更加完善,那就是eval()函數,只需將要計算的表達式傳遞給eval函數即可算出結果。看到這里,是不是有點淚奔的感覺,白寫了。其實不然,通過我們自己寫,可以更好的理解實現的原理,並且加強自己寫代碼的能力。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM