用python實(shí)現(xiàn)一個(gè)簡(jiǎn)單計(jì)算器(完整DEMO)
一、功能目標(biāo)
用戶輸入一個(gè)類似 1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2)) 這樣的表達(dá)式,假設(shè)表達(dá)式里面除了包含空格、'+'、'-'、'*'、'/'和括號(hào)再無(wú)其他特殊符號(hào),然后自己動(dòng)手寫代碼解析其中的表達(dá)式,實(shí)現(xiàn)加減乘除,最后得出的結(jié)果與真實(shí)的計(jì)算機(jī)所算的結(jié)果必須一致。
二、解題思路
1、為了分開運(yùn)算符和數(shù)字,因此把輸入的字符串格式轉(zhuǎn)換為 列表的格式進(jìn)行處理,這樣子就可以按位進(jìn)行 處理了
2、實(shí)現(xiàn)功能的核心點(diǎn)在于括號(hào)、乘除、加減的優(yōu)先級(jí)排序,因此我們先想辦法一層一層的去括號(hào),即從最里層的括號(hào)開始計(jì)算,然后去掉第一層括號(hào),然后一直繼續(xù)這個(gè) 過(guò)程,最后得到一個(gè)沒(méi)有括號(hào)的列表,再進(jìn)行計(jì)算得出結(jié)果
3、去括號(hào)方式:最內(nèi)層的括號(hào)內(nèi)的表達(dá)式就可以當(dāng)做一個(gè)無(wú)括號(hào)表達(dá)式,通過(guò)先 運(yùn)算出乘除,再運(yùn)算出加減得出整個(gè)括號(hào)內(nèi)的值,用這個(gè)結(jié)果值整體替換括號(hào)內(nèi)的內(nèi)容即實(shí)現(xiàn)了去一層括號(hào),然后通過(guò)遞歸去除所有的括號(hào)
4、去除乘除號(hào)方式:見(jiàn) remove_multiplication_division(eq) 函數(shù)部分
5、去除加減號(hào) 方式:見(jiàn) remove_plus_minus(eq) 函數(shù)部分
三、函數(shù)說(shuō)明
1、主函數(shù)
def caculator(eq): format_list = eq_format(eq) # 把字符串變成格式化列表形式 s_eq = simplify(format_list) # 去括號(hào),得到無(wú)括號(hào)的一個(gè)格式化列表 ans = calculate(s_eq) # 計(jì)算最終結(jié)果 if len(ans) == 2: # 判斷最終結(jié)果為正數(shù)還是負(fù)數(shù) ans = -float(ans[1]) else: ans = float(ans[0]) return ans
2、eq_format( )函數(shù)
def eq_format(eq): ''' :param eq: 輸入的算式字符串 :return: 格式化以后的列表,如['60','+','7','*','8'] ''' format_list = re.findall('[\d\.]+|\(|\+|\-|\*|\/|\)',eq) return format_list
2.1 這個(gè)函數(shù)的作用是把輸入的算式通過(guò)re模塊,用正則表達(dá)式把算術(shù)符號(hào)和數(shù)字分開。
2.2 [\d\.]+ | \( | \+ | \- | \* | \/ | \)意思:按管道符號(hào) | (| 表示 或 的意思)可分為幾部分,[\d\.]+ 是指匹配數(shù)字或小數(shù)點(diǎn)一次或多次,\(是指左括號(hào),\+ 是指 加號(hào),\- 是指減號(hào),\* 是指乘號(hào),\/ 是指除號(hào), \) 是指右括號(hào),整個(gè)正則表達(dá)式會(huì)把字符串變成類似['(','6','*','5','-''7',')' ]這樣子的格式列表
3、simplify( ) 函數(shù)
def simplify(format_list): ''' :param format_list: 輸入的算式格式化列表如['60','+','7','*','8'] :return: 通過(guò)遞歸去括號(hào),返回簡(jiǎn)化后的列表 ''' bracket = 0 # 用于存放左括號(hào)在格式化列表中的索引 count = 0 for i in format_list: if i == '(': bracket = count elif i == ')': temp = format_list[bracket + 1 : count] # print(temp) new_temp = calculate(temp) format_list = format_list[:bracket] + new_temp + format_list[count+1:] format_list = change(format_list,bracket) # 解決去括號(hào)后會(huì)出現(xiàn)的-- +- 問(wèn)題 return simplify(format_list) # 遞歸去括號(hào) count = count + 1 return format_list # 當(dāng)遞歸到最后一層的時(shí)候,不再有括號(hào),因此返回列表
3.1 這個(gè)函數(shù)的作用是:把輸入的帶有括號(hào)的格式化列表,用遞歸的方式去除括號(hào),每一次遞歸去一個(gè)括號(hào), 直到?jīng)]有括號(hào)則返回去完括號(hào)的格式化列表
3.2 找到最內(nèi)層括號(hào)的方法:遍歷列表,如果遇到左括號(hào),則把當(dāng)前左括號(hào)的索引賦值給參數(shù)bracket,直到遇到第一個(gè)右括號(hào),此時(shí)的索引與bracket中間的元素即為最內(nèi)層括號(hào)的元素,用切片的方式提取出來(lái),通過(guò) calculate() 函數(shù)計(jì)算出值,然后用計(jì)算結(jié)果去替換掉此時(shí)左括號(hào)到第一個(gè)右括號(hào)的元素,此時(shí)去除第一層括號(hào),然后進(jìn)入遞歸,不斷遞歸直至去除所有括號(hào)
3.3 可能遇到的問(wèn)題:
首先是不要用index的方式去取當(dāng)前左括號(hào)的索引,因?yàn)榱斜淼膇ndex方法返回的一直都是第一個(gè)左括號(hào)的索引,而不是當(dāng)前左括號(hào)的索引,會(huì)導(dǎo)致出錯(cuò)。因此我在函數(shù)內(nèi)用參數(shù) count 進(jìn)行計(jì)數(shù)當(dāng)前索引值。
然后是用計(jì)算得出的值來(lái)替換掉第一層括號(hào)部分后,有可能會(huì)出現(xiàn) ‘+-' ,‘ - -'的情況,要記得處理,我的函數(shù)中寫了一個(gè)change() 函數(shù)進(jìn)行處理
4、caculate()函數(shù)
def calculate(s_eq): ''' :param s_eq: 不帶括號(hào)的格式化列表 :return: 計(jì)算結(jié)果 ''' if '*' or '/' in s_eq: s_eq = remove_multiplication_division(s_eq) if '+' or '-' in s_eq: s_eq = remove_plus_minus(s_eq) return s_eq
這個(gè)函數(shù)的作用是輸入不帶括號(hào)的格式化列表, 輸出計(jì)算結(jié)果,然后返回結(jié)果列表
思路是先算乘除:remove_multiplication_division()函數(shù),然后再?gòu)念^到尾計(jì)算加減法:remove_plus_minus( )函數(shù)
5、remove_multiplication_division()函數(shù)
def remove_multiplication_division(eq): ''' :param eq: 帶有乘除號(hào)的格式化列表 :return: 去除了乘除號(hào)的格式化列表 ''' count = 0 for i in eq: if i == '*': if eq[count+1] != '-': eq[count-1] = float(eq[count-1]) * float(eq[count+1]) del(eq[count]) del(eq[count]) elif eq[count+1] == '-': eq[count] = float(eq[count-1]) * float(eq[count+2]) eq[count-1] = '-' del(eq[count+1]) del(eq[count+1]) eq = change(eq,count-1) return remove_multiplication_division(eq) elif i == '/': if eq[count+1] != '-': eq[count-1] = float(eq[count-1]) / float(eq[count+1]) del(eq[count]) del(eq[count]) elif eq[count+1] == '-': eq[count] = float(eq[count-1]) / float(eq[count+2]) eq[count-1] = '-' del(eq[count+1]) del(eq[count+1]) eq = change(eq,count-1) return remove_multiplication_division(eq) count = count + 1 return eq
這個(gè)函數(shù)的作用是計(jì)算乘除,去乘除號(hào)。方法也是遞歸的方法,每次處理完一個(gè)乘號(hào)或者除號(hào)都可能出現(xiàn)符號(hào)問(wèn)題,記得處理后再進(jìn)入下一次遞歸
6、remove_plus_minus( )函數(shù)
def remove_plus_minus(eq): ''' :param eq: 只帶有加減號(hào)的格式化列表 :return: 計(jì)算出整個(gè)列表的結(jié)果 ''' count = 0 if eq[0] != '-': sum = float(eq[0]) else: sum = 0.0 for i in eq: if i == '-': sum = sum - float(eq[count+1]) elif i == '+': sum = sum + float(eq[count+1]) count = count + 1 if sum >= 0: eq = [str(sum)] else: eq = ['-',str(-sum)] return eq
這個(gè)函數(shù)輸入一個(gè)只有加減號(hào)的格式化列表,然后從頭到尾的計(jì)算,得出最終結(jié)果,返回最終結(jié)果(結(jié)果形式也是列表)
7、change()函數(shù)
def change(eq,count): ''' :param eq: 剛?cè)ネ昀ㄌ?hào)或者乘除后的格式化列表 :param count: 發(fā)生變化的元素的索引 :return: 返回一個(gè)不存在 '+-' ,'--'類的格式化列表 ''' if eq[count] == '-': if eq[count-1] == '-': eq[count-1] = '+' del eq[count] elif eq[count-1] == '+': eq[count-1] = '-' del eq[count] return eq
這個(gè)函數(shù)的作用是解決符號(hào)輸入兩個(gè)問(wèn)題。輸入?yún)?shù)1:剛?cè)ネ昀ㄌ?hào)的或者剛 計(jì)算完乘除的 格式化列表,輸入?yún)?shù)2: 列表元素發(fā)生變化的索引(如去括號(hào)時(shí),這個(gè)索引則為去括號(hào)前列表的最內(nèi)層左括號(hào)的索引),輸出結(jié)果是一個(gè)處理完符號(hào)問(wèn)題或者什么都不做直接返回的列表
四、完整代碼
需要的小伙伴,請(qǐng)關(guān)注微信公眾號(hào): Python客棧, 或者掃描下方公眾號(hào)二維碼,回復(fù)關(guān)鍵字:計(jì)算器, 即可免費(fèi)無(wú)套路獲取
↑關(guān)注上方公眾號(hào)回復(fù)計(jì)算器 即可↑
程序運(yùn)行結(jié)果
相關(guān)文章
Python技巧之實(shí)現(xiàn)批量統(tǒng)一圖片格式和尺寸
大家在工作的時(shí)候基本都會(huì)接觸到很多的圖片,有時(shí)為了不同的工作需求需要修改圖片的尺寸或者大小。本文為大家整理了Python批量轉(zhuǎn)換圖片格式和統(tǒng)一圖片尺寸,希望對(duì)大家有所幫助2023-05-05Python實(shí)現(xiàn)ping指定IP的示例
今天小編就為大家分享一篇Python實(shí)現(xiàn)ping指定IP的示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-06-06python網(wǎng)絡(luò)應(yīng)用開發(fā)知識(shí)點(diǎn)淺析
在本篇內(nèi)容中小編給學(xué)習(xí)python的朋友們整理了關(guān)于網(wǎng)絡(luò)應(yīng)用開發(fā)的相關(guān)知識(shí)點(diǎn)以及實(shí)例內(nèi)容,需要的朋友們參考下。2019-05-05Python3 關(guān)于pycharm自動(dòng)導(dǎo)入包快捷設(shè)置的方法
今天小編就為大家分享一篇Python3 關(guān)于pycharm自動(dòng)導(dǎo)入包快捷設(shè)置的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01Python Celery多隊(duì)列配置代碼實(shí)例
這篇文章主要介紹了Python Celery多隊(duì)列配置代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11詳解Python+Pyecharts實(shí)現(xiàn)漏斗圖的繪制
漏斗圖是一個(gè)簡(jiǎn)單的散點(diǎn)圖,反映研究在一定樣本量或精確性下單個(gè)研究的干預(yù)效應(yīng)估計(jì)值。本文將用Python Pyecharts實(shí)現(xiàn)漏斗圖的繪制,需要的可以參考一下2022-06-06對(duì)PyQt5中樹結(jié)構(gòu)的實(shí)現(xiàn)方法詳解
今天小編就為大家分享一篇對(duì)PyQt5中樹結(jié)構(gòu)的實(shí)現(xiàn)方法詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06Python實(shí)現(xiàn)的多進(jìn)程和多線程功能示例
這篇文章主要介紹了Python實(shí)現(xiàn)的多進(jìn)程和多線程功能,結(jié)合實(shí)例形式分析了Python多線程與多進(jìn)程實(shí)現(xiàn)分布式系統(tǒng)功能相關(guān)操作技巧,需要的朋友可以參考下2018-05-05Linux下將Python的Django項(xiàng)目部署到Apache服務(wù)器
這篇文章主要介紹了Python的Django項(xiàng)目部署到Apache服務(wù)器上的要點(diǎn)總結(jié),文中針對(duì)的是wsgi連接方式,需要的朋友可以參考下2015-12-12安裝pyecharts1.8.0版本后導(dǎo)入pyecharts模塊繪圖時(shí)報(bào)錯(cuò): “所有圖表類型將在 v1.9.0 版本開始
這篇文章主要介紹了安裝pyecharts1.8.0版本后導(dǎo)入pyecharts模塊繪圖時(shí)報(bào)錯(cuò): “所有圖表類型將在 v1.9.0 版本開始強(qiáng)制使用 ChartItem 進(jìn)行數(shù)據(jù)項(xiàng)配置 ”的解決方法,需要的朋友可以參考下2020-08-08