用python實(shí)現(xiàn)一個(gè)簡(jiǎn)單計(jì)算器(完整DEMO)
一、功能目標(biāo)
用戶輸入一個(gè)類(lèi)似 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)手寫(xiě)代碼解析其中的表達(dá)式,實(shí)現(xiàn)加減乘除,最后得出的結(jié)果與真實(shí)的計(jì)算機(jī)所算的結(jié)果必須一致。
二、解題思路
1、為了分開(kāi)運(yùn)算符和數(shù)字,因此把輸入的字符串格式轉(zhuǎn)換為 列表的格式進(jìn)行處理,這樣子就可以按位進(jìn)行 處理了
2、實(shí)現(xiàn)功能的核心點(diǎn)在于括號(hào)、乘除、加減的優(yōu)先級(jí)排序,因此我們先想辦法一層一層的去括號(hào),即從最里層的括號(hào)開(kāi)始計(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ù)字分開(kāi)。
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ì)把字符串變成類(lèi)似['(','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ù)中寫(xiě)了一個(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è)不存在 '+-' ,'--'類(lèi)的格式化列表
'''
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 使用 selenium 進(jìn)行自動(dòng)化測(cè)試或者協(xié)助日常工作
這篇文章主要介紹了Python 使用 selenium 進(jìn)行自動(dòng)化測(cè)試 或者協(xié)助日常工作,我們可以使用 selenium 來(lái)幫助我們進(jìn)行自動(dòng)化的 Web 測(cè)試,也可以通過(guò) selenium 操作瀏覽器做一些重復(fù)的,簡(jiǎn)單的事情,來(lái)減輕我們的工作2021-09-09
python orm 框架中sqlalchemy用法實(shí)例詳解
這篇文章主要介紹了python orm 框架中sqlalchemy用法,結(jié)合實(shí)例形式詳細(xì)分析了Python orm 框架基本概念、原理及sqlalchemy相關(guān)使用技巧,需要的朋友可以參考下2020-02-02
一文帶你理解Python中import機(jī)制與importlib的妙用
在Python編程的世界里,import語(yǔ)句是開(kāi)發(fā)者最常用的工具之一,它就像一把鑰匙,打開(kāi)了通往各種功能和庫(kù)的大門(mén),下面就跟隨小編一起來(lái)學(xué)習(xí)一下import機(jī)制的具體使用吧2025-01-01
使用python連接Linux服務(wù)器發(fā)送指定命令的示例代碼
這篇文章主要介紹了使用python連接Linux服務(wù)器發(fā)送指定命令,首先安裝paramiko庫(kù),使用paramiko庫(kù)連接linux,使用paramiko庫(kù)上傳下載文件,結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10
Python寫(xiě)一個(gè)字符串?dāng)?shù)字后綴部分的遞增函數(shù)
這篇文章主要介紹了Python寫(xiě)一個(gè)字符串?dāng)?shù)字后綴部分的遞增函數(shù),寫(xiě)函數(shù)之前需要Python處理重名字符串,添加或遞增數(shù)字字符串后綴,下面具體過(guò)程,需要的小伙伴可以參考一下2022-03-03

