如何制作python代码_如何使用50行Python代码制作一个计算器
(點(diǎn)擊上方公號(hào),可快速關(guān)注)
作者:Stories For Sad Robots
譯者:開(kāi)源中國(guó)社區(qū)
鏈接:http://www.oschina.net/translate/how-to-write-a-calculator-in-50-python-lines-without-eval
簡(jiǎn)介
在這篇文章中,我將向大家演示怎樣向一個(gè)通用計(jì)算器一樣解析并計(jì)算一個(gè)四則運(yùn)算表達(dá)式。當(dāng)我們結(jié)束的時(shí)候,我們將得到一個(gè)可以處理諸如 1 2*-(-3 2)/5.6 3樣式的表達(dá)式的計(jì)算器了。當(dāng)然,你也可以將它拓展的更為強(qiáng)大。
我本意是想提供一個(gè)簡(jiǎn)單有趣的課程來(lái)講解 語(yǔ)法分析 和 正規(guī)語(yǔ)法(編譯原理內(nèi)容)。同時(shí),介紹一下PlyPlus,這是一個(gè)我斷斷續(xù)續(xù)改進(jìn)了好幾年的語(yǔ)法解析 接口。作為這個(gè)課程的附加產(chǎn)物,我們最后會(huì)得到完全可替代eval()的一個(gè)安全的四則運(yùn)算器。
如果你想在自家的電腦上試試本文中給的例子的話,你應(yīng)該先安裝 PlyPlus ,使用命令pip install plyplus 。(譯者注:pip是一個(gè)包管理系統(tǒng),用來(lái)安裝用python寫的軟件包,具體使用方法大家可以百度之或是google之,就不贅述了。)
本篇文章需要對(duì)python的繼承使用有所了解。
語(yǔ)法
對(duì)于那些不懂的如何解析和正式語(yǔ)法工作的人而言,這里有一個(gè)快速的概覽:正式語(yǔ)法是用來(lái)解析文本的一些不同層面的規(guī)則。每一個(gè)規(guī)則都描述了相對(duì)應(yīng)的那部分輸入的文本是如何組成的。
這里是一個(gè)用來(lái)展示如何解析1 2 3 4的例子:
Rule#1 - add IS MADE OF add number
ORnumbernumber
或者用 EBNF:
add:add' 'number
|number' 'number
;
解析器每次都會(huì)尋找add number或者number number,找到一個(gè)之后就會(huì)將其轉(zhuǎn)換成add。基本上而言,每一個(gè)解析器的目標(biāo)都在于盡可能的找到最高層次的表達(dá)式抽象。
以下是解析器的每個(gè)步驟:
number number number number
第一次轉(zhuǎn)換將所有的Number變成“number”規(guī)則
[number number] number number
解析器找到了它的第一個(gè)匹配模式!
[add number] number
在轉(zhuǎn)換成一個(gè)模式之后,它開(kāi)始尋找下一個(gè)
[add number]
add
這些有次序的符號(hào)變成了一個(gè)層次上的兩個(gè)簡(jiǎn)單規(guī)則: number number和add number。這樣,只需要告訴計(jì)算機(jī)如果解決這兩個(gè)問(wèn)題,它就能解析整個(gè)表達(dá)式。事實(shí)上,無(wú)論多長(zhǎng)的加法序列,它都能解決! 這就是形式文法的力量。
運(yùn)算符優(yōu)先級(jí)
算數(shù)表達(dá)式并不僅僅是符號(hào)的線性增長(zhǎng),運(yùn)算符創(chuàng)造了一個(gè)隱式的層次結(jié)構(gòu),這非常適合用形式文法來(lái)表示:
1 2 * 3 / 4 5 6
這相當(dāng)于:
1 (2 * 3 / 4) 5 6
我們可以通過(guò)嵌套規(guī)則表示此語(yǔ)法中的結(jié)構(gòu):
add:addmul
|mul' 'mul
;
mul:mul'*; number
| number'*'number
;
通過(guò)將add設(shè)為操作mul而不是number,我們就得到了乘法優(yōu)先的規(guī)則。
讓我們?cè)谀X海中模擬一下使用這個(gè)神奇的解析器來(lái)分析1 2*3*4的過(guò)程:
number number * number * number
number [number * number] * number
解析器不知道number number的結(jié)果,所以這是它(解析器)的另一個(gè)選擇
number [mul * number]
number mul
???
現(xiàn)在我們遇到了一點(diǎn)困難! 解析器不知道如何處理number mul。我們可以區(qū)分這種情況,但是如果我們繼續(xù)探索下去,就會(huì)發(fā)現(xiàn)有很多不同的沒(méi)有考慮到得可能,比如mul number, add number, add add, 等等。
那么我們應(yīng)該怎么做呢?
幸運(yùn)的是,我們可以做一點(diǎn)小“把戲”:我們可以認(rèn)為一個(gè)number本身是一個(gè)乘積,并且一個(gè)乘積本身是一個(gè)和!
這種思路一開(kāi)始看起來(lái)有點(diǎn)古怪,不過(guò)它的確是有意義的:
add:add' 'mul
|mul' 'mul
|mul
;
mul:mul'*'number
|number'*'number
|number
;
但是如果 mul能夠變成 add, 且 number能夠變成 mul , 有些行的內(nèi)容就變得多余了。丟棄它們,我們就得到了:
add:add' 'mul
|mul
;
mul:mul'*'number
|number
;
讓我們來(lái)使用這種新的語(yǔ)法來(lái)模擬運(yùn)行一下1 2*3*4:
number number * number * number
現(xiàn)在沒(méi)有一個(gè)規(guī)則是對(duì)應(yīng)number*number的了,但是解析器可以“變得有創(chuàng)造性”
number [number] * number * number
number [mul * number] * number
number [mul * number]
[number] mul
[mul] mul
[add mul]
add
成功了!!!
如果你覺(jué)得這個(gè)很奇妙,那么嘗試著去用另一種算數(shù)表達(dá)式來(lái)模擬運(yùn)行一下,然后看看表達(dá)式是如何用正確的方式來(lái)一步步解決問(wèn)題的。或者等著閱讀下一節(jié)中的內(nèi)容,看看計(jì)算機(jī)是如何一步步運(yùn)行出來(lái)的!
運(yùn)行解析器
現(xiàn)在我們對(duì)于如何讓我們的語(yǔ)法運(yùn)作起來(lái)已經(jīng)有了非常不錯(cuò)的想法了,那就寫一個(gè)實(shí)際的語(yǔ)法來(lái)應(yīng)用一下吧:
start:add;// 這是最高層
add:add add_symbolmul|mul;
mul:mul mul_symbolnumber|number;
number:'[d.] ';// 十進(jìn)制數(shù)的正則表達(dá)式
mul_symbol:'*'|'/';// Match * or /
add_symbol:' '|'-';// Match or -
你可能想要復(fù)習(xí)一下正則表達(dá)式,但不管怎樣,這個(gè)語(yǔ)法都非常直截了當(dāng)。讓我們用一個(gè)表達(dá)式來(lái)測(cè)試一下吧:
Tag標(biāo)簽:
總結(jié)
以上是生活随笔為你收集整理的如何制作python代码_如何使用50行Python代码制作一个计算器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 源 arm_arm和X86处理器性能简单
- 下一篇: 截取小数点_五年级上册系列| 小数点位置