使用Java實(shí)現(xiàn)一個lambda interpreter

Lambda Interpreter

A λ-calculus interpreter
interpreter used Java by Pkun


我們利用自頂向下的思考方式呢簸,首先搀突,輸入是一個lambda表達(dá)式,為了方便起見柳骄,我們將lambda寫作\团赏,所以輸入的式子應(yīng)該是這樣。
(\x.\y (x y)) (\p.p)(\q.q)
那么改如何去解釋它呢耐薯?我們先用常規(guī)的方法計(jì)算一下舔清。很容易看出我們只需要把后面兩個抽象帶入到前面的抽象就可以了。如果是計(jì)算機(jī)做的話曲初,上面這句話可以分解成

  • 識別出三個抽象
  • 將后面的抽象按照順序帶入到前面的抽象中

我們先復(fù)習(xí)一下之前學(xué)過的lambda演算的知識

t1 t2   # Application
 
\x. t1  # Abstraction
 
x       # Identifier

如果要一般化体谒,第一步可以理解成

  • 計(jì)算機(jī)必須能夠翻譯lambda表達(dá)式中的所有項(xiàng),分別是App臼婆,Abs和Ide抒痒。

那么該如何翻譯出所有的項(xiàng)呢冰抢?我們注意到他們?nèi)齻€各有特點(diǎn)考榨,Abs中有l(wèi)ambda熏版,Application有兩支旷坦,Identifier只有一個值似谁,另一方面奖磁,注意到我們讀入的是一個字符串拾并,所以我們可以采用從頭到尾遍歷整個字符串的方法蹦渣,將字符串中的所有項(xiàng)都讀出來誓酒。
到這里惨缆,我們要注意到的一件事情就是Application,Abstraction是可以相互嵌套的丰捷,Identifier也可以是Application的左右兩支坯墨,最后出來的應(yīng)該是一個樹狀的結(jié)構(gòu),到這里病往,我們不難想到讓三個類都繼承一個節(jié)點(diǎn)類捣染,通過節(jié)點(diǎn)訪問節(jié)點(diǎn)的節(jié)點(diǎn),來遍歷整棵樹停巷,避免嵌套帶來的類型轉(zhuǎn)換的麻煩耍攘。

我們規(guī)定一個抽象語法樹,它的節(jié)點(diǎn)有三個畔勤,抽象蕾各,應(yīng)用和原子。

既然要遍歷整個字符串庆揪,我們采用枚舉的方式式曲,一旦符合某種模式,就將他歸類為某種語法,所以我們需要規(guī)定一個lambda表達(dá)式中會出現(xiàn)的所有字符吝羞,其實(shí)也很簡單兰伤,是下面六種

LPAREN: '('
RPAREN: ')'
LAMBDA: '\' // 為了方便使用 “\”
DOT: '.'
LCID: /[a-z][a-zA-Z]*/ 
EOF: null

我們將這些字符命名為Token,并且通過識別不同的Token钧排,把一個Lambda表達(dá)式解析成一個抽象語法樹敦腔。到這里,我們已經(jīng)構(gòu)建出一個計(jì)算機(jī)可以閱讀的Lambda表達(dá)式了恨溜。接下來我們要讓計(jì)算機(jī)在遍歷的過程中符衔,把整個抽象語法樹構(gòu)建出來。為了方便起見糟袁,我們制定三條語法規(guī)則:

Term ::= Application| LAMBDA LCID DOT Term

Application ::= Application Atom| Atom

Atom ::= LPAREN Term RPAREN| LCID

整體的思路就是不斷讀入下一個字符判族,判斷它是Token中的哪一種,然后在Term Application Atom三個方法之中跳轉(zhuǎn)系吭,構(gòu)建出整個樹五嫂。

接下來讓我們考慮第二步颗品,也就是做beta規(guī)約的事情肯尺。其實(shí)想想還是挺容易的一件事情,因?yàn)樵谖覀兊某橄笳Z法樹里面躯枢,如果某個節(jié)點(diǎn)的左支是Abstraction则吟,你可以直接把右支帶入到左支里面去,替換左支body中的所有和param一樣的東西锄蹂。寫成偽碼的話應(yīng)該是這樣子

while(hasnext(Abs.Body)){
  if(charInBody==Abs.Param) charInBody = Ide;
}

筆者認(rèn)為也這是一種比較好的處理方式氓仲,但是很多細(xì)節(jié)問題沒有考慮,也不知道這種處理方式最后效果怎么樣得糜。按道理來說是不會出現(xiàn)因?yàn)閮蓚€Abs的param相同而導(dǎo)致代入出現(xiàn)問題的敬扛。接下來我要介紹老師欽定的方法來構(gòu)造求值的函數(shù)。

De Bruijn index

關(guān)于De Bruijn,你可以在下面網(wǎng)站中找到你想要的 De Bruijn Sequence(無法查看的話可以百度百科)
用De Bruijin Index朝抖,我們可以給Abs或者Ide中的項(xiàng)打上自己的標(biāo)簽啥箭,比如說\x.\y x y 可以看成 \x.\y 1 0,但是對于\x.a 這樣的變量沒有綁定的時候治宣,可能是默認(rèn)換成\x. 1吧(如果博客沒寫錯的話)急侥。印入De Bruijn Index最主要的原因是alpha替換,lambda表達(dá)式中的符號是可以隨意替換的侮邀,它并沒有具體的含義坏怪,是一種假變量,就算重復(fù)也沒有關(guān)系绊茧,當(dāng)然铝宵,在同一個abs或者其他的一些式子之中,我們?yōu)榱藚^(qū)分和計(jì)算還是應(yīng)該將變量取上不同的名字华畏。

當(dāng)然捉超,運(yùn)用了De Bruijn Index之后胧卤,我們的前一個階段又出現(xiàn)了一個問題,我們考慮這樣的一個抽象

\x.\y.x (\x. x)

如果你要給上述的抽象進(jìn)行De Bruijn轉(zhuǎn)換的話拼岳,內(nèi)層的x的序號應(yīng)該是多少呢枝誊?我們說,它轉(zhuǎn)換后應(yīng)該是這個樣子

\.\.1 (\. 0)

因?yàn)閮?nèi)層的abs綁定的變量就算名字和外層的一樣惜纸,但是我們認(rèn)為名字是無關(guān)的叶撒,所以應(yīng)該重新計(jì)算內(nèi)層abs的De Bruijn值。
這里我們要引入一個上下文存儲的結(jié)構(gòu)耐版,叫做ctx祠够,運(yùn)用它我們就可以很好的解決這個內(nèi)外層param相同,De Bruijn不同的問題粪牲。

使用De Bruijn Index之后古瓤,當(dāng)你替換了之后,你可能還存在于方法棧的頂端腺阳,意思就是被替換的Abs還沒有被完全改變落君,這時候,有可能產(chǎn)生一種誤解亭引,比如下面這樣:
Abs = \x.\y.x = ..1
ide = \t.a = \t.1
替換后Abs 將會在這個方法還沒跳出棧之前被設(shè)置為 \x.\y.\t.1 這個時候就會產(chǎn)生誤解绎速,這個1到底是指的x還是指的a呢?所以我們要引入一些新的方法焙蚓,來解決這個問題纹冤。我們將\t.1升序,升到\t.2购公,然后帶入到Abs中萌京,然后我們考慮代入的深度,因?yàn)橹贿M(jìn)行一次帶入的話我們只需要考慮最外層會不會產(chǎn)生沖突宏浩,如果被你替換的那個值正好是最外層的值知残,這表明你需要再對你的\t.2進(jìn)行一次升序,升到不會產(chǎn)生沖突绘闷,升多少呢橡庞?保險起見,我們應(yīng)該升到超過它的深度印蔗,這樣就不會產(chǎn)生任何誤解扒最。最后,我們再將一開始升序的那一次降下來华嘹,這樣整個替換的過程就完成了吧趣,也不會出現(xiàn)任何問題。

這樣,整個lambda解釋器的基本原理就已經(jīng)講解完了强挫,其他就是代碼怎么寫了岔霸,不過代碼難度其實(shí)也挺小的,你甚至都不需要知道代碼背后是怎么運(yùn)行的俯渤,而是按部就班的按照上面所寫的慢慢敲出來呆细,最后也可以通過OJ,同時在寫代碼的時候八匠,我發(fā)現(xiàn)也并沒有涉及到是否產(chǎn)生誤解這件事情(如果采用我上面的思路的話)絮爷,因此我在想是否對于計(jì)算機(jī)來說,本來這個誤解就不會產(chǎn)生呢梨树?等有空的時候我會用自己的思路重寫一遍代碼坑夯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市抡四,隨后出現(xiàn)的幾起案子柜蜈,更是在濱河造成了極大的恐慌,老刑警劉巖指巡,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淑履,死亡現(xiàn)場離奇詭異,居然都是意外死亡厌处,警方通過查閱死者的電腦和手機(jī)鳖谈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進(jìn)店門岁疼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阔涉,“玉大人,你說我怎么就攤上這事捷绒」迮牛” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵暖侨,是天一觀的道長椭住。 經(jīng)常有香客問我,道長字逗,這世上最難降的妖魔是什么京郑? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮葫掉,結(jié)果婚禮上些举,老公的妹妹穿的比我還像新娘。我一直安慰自己俭厚,他們只是感情好户魏,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般叼丑。 火紅的嫁衣襯著肌膚如雪关翎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天鸠信,我揣著相機(jī)與錄音纵寝,去河邊找鬼。 笑死星立,一個胖子當(dāng)著我的面吹牛店雅,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贞铣,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼闹啦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了辕坝?” 一聲冷哼從身側(cè)響起窍奋,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎酱畅,沒想到半個月后琳袄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纺酸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年窖逗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片餐蔬。...
    茶點(diǎn)故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡碎紊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出樊诺,到底是詐尸還是另有隱情仗考,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布词爬,位于F島的核電站秃嗜,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏顿膨。R本人自食惡果不足惜锅锨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恋沃。 院中可真熱鬧必搞,春花似錦、人聲如沸芽唇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至研侣,卻和暖如春谱邪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背庶诡。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工惦银, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人末誓。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓扯俱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親喇澡。 傳聞我的和親對象是個殘疾皇子迅栅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評論 2 348