編程語言的基石——Lambda calculus

https://liujiacai.net/blog/2014/10/12/lambda-calculus-introduction/

Lambda calculus我們一般稱為λ演算,最早是由邱奇(Alonzo Church,圖靈的博導)在20世紀30年代引入,當時的背景是解決函數可計算的本質性問題,初期λ演算成功的解決了在可計算理論中的判定性問題,后來根據Church–Turing thesis,證明了λ演算與圖靈機是等價的。

演算的語法與求值

語法(syntax)

因為λ演算研究的是函數的本質性問題,所以形式極其簡單:

E = x? ? ? ? ? variables

| λx. Efunctioncreation(abstraction)

| E1 E2functionapplication

上面的E稱為λ-表達式(expressions)或λ-terms,它的值有三種形式:

1,變量(variables)。

2,函數聲明或抽象(function creation/abstraction)。需要注意是的,函數中有且僅有一個參數。在λx. E中,x是參數,E是函數體

3,函數應用(function application)。也就是我們理解的函數調用,但官方術語就叫函數應用,本文后面也會采用“應用”的叫法。

λ表達式例子

上面就是λ演算的語法了,很是簡單吧。下面看幾個例子:

恒等函數

λx.x

一個返回恒等函數的函數

λy. (λx.x)

可以看到,這里的y參數直接被忽略了

在使用λ演算時,有一些慣例需要說一下:

1,函數聲明時,函數體盡可能的向右擴展。什么意思呢,舉個例子大家就明白了

λx.xλy.xy z 應該理解為 λx. (x(λy. ((xy) z)))

2,函數應用時,遵循左結合。在舉個例子:

x y z 應該解釋為 (x y) z


Currying帶有多個參數的函數

從上面我們知道,λ演算中函數只有一個參數,那兩個參數的函數的是不是就沒法表示了呢,那λ演算的功能也太弱了吧,這就是λ的神奇之處,函數在本質上只需要一個參數即可。如果想要聲明多個參數的函數,通過currying技術即可。下面來說說currying。

λx y. (+ x y)---->λx. (λ y. + x y)

上面這個轉化就叫currying,它展示了,我們如何實現加法(這里假設+這個符號已經具有相加的功能,后面我們會講到如何用λ表達式來實現這個+的功能)。

其實就是我們現在意義上的閉包——你調用一個函數,這個函數返回另一個函數,返回的函數中存儲保留了調用函數的變量。currying是閉包的鼻祖。

如果用Python來表示就是這樣的東西:

def add(x):

????????return? ?lambday: x+y

add(4)(3) //return7


如果用函數式語言clojure來表示就是:

(def nadd [x]

????(fn [y] (+ x y)))

((add 4) 3); return 7

求值(evaluation)

在λ演算中,有兩條求值規則:

1,Alpha equivalence( or conversion )

2,Beta reduction

Alpha equivalence

這個比較簡單也好理解,就是說λx.x與λy.y是等價的,并不因為換了變量名而改變函數的意義。

簡單并不說這個規則不重要,在一些變量覆蓋的場合很重要,如下這個例子:

λx. x (λx. x)如果你這么寫的話,第二個函數定義中的x與第一個函數定義中的x重復了,也就是在第二個函數里把第一個的x給覆蓋了。

如果改為λx. x (λy. y)就不會有歧義了。

Beta reduction

這個規則是λ演算中函數應用的重點了。一句話來解釋就是,把參數應用到函數體中。舉一個例子:

有這么一個函數應用(λx.x)(λy.y),在這里把(λy.y)帶入前面函數的x中,就能得到最終的結果(λy.y),這里傳入一個函數,然后又返回一個函數,這就是最終的結果。

求值順序

考慮下面這個函數應用

(λ y. (λ x. x) y) E

有兩種計算方法,如下圖

可以先計算內層的函數調用再計算外層的函數調用,反之也可。

根據Church–Rosser定理,這兩種方法是等價的,最終會得到相等的結果,如上圖最后都得到了E。

但如果我們要自己實現一種語言,就有可能必選二選其一,于是有了下面兩種方式:

1,Call by Value(Eager Evaluation及早求值)

也就是上圖中的inner,這種方式在函數應用前,就計算函數參數的值。如:

(λy. (λx. x)y)((λu. u)(λv. v)) --->

(λy. (λx. x)y)(λv. v)--->

(λx. x)(λv. v)--->

λv. v

2,Call by Name (Lazy Evaluation惰性求值)

也就是上圖中的outer,這種方式在函數應用前,不計算函數參數的值,直到需要時才求值。如:

(λy. (λx. x)y)((λu. u)(λv. v)) --->

(λx. x)((λu. u)(λv. v)) --->

(λu. u)(λv. v)--->

λv. v

值得一提的是,Call by Name這種方式在我們目前的語言中,只有函數式語言支持。

λ演算與編程語言的關系

在λ演算中只有函數(變量依附于函數而有意義),如果要用純λ演算來實現一門編程語言的話,我們還需要一些數據類型,比如boolean、number、list等,那怎么辦呢?

λ的強大又再一次展現出來,所有的數據類型都能用函數模擬出來,秘訣就是

不要去關心數據的值是什么,重點是我們能對這個值做什么操作,然后我們用合法的λ表達式把這些操作表示出來即可。

聽上去很些云里霧里,但看了我下面的講解以后,你會發現,編程語言原來還可以這么玩,希望我能把這部分講清楚些,個人感覺這些東西太funny了 :-)

好了,我們先從最簡單——boolean的開始。

編碼Boolean

Ask:我們能對boolean值做什么?

Answer:我們能夠進行條件判斷,二選其一。

好,知道了能對boolean的操作,下面就用λ表達式來定義它:

true= λx. λy. x

false= λx. λy. y

if E1 then E2 else E3= E1 E2 E3

來簡單解釋一下,boolean就是這么一個函數,它有兩個參數(通過currying實現),返回其中一個。下面看個例子:

if true then u else v 可以寫成

(λx. λy. x) u v --->(λy. u) v --->u

哈哈,很神奇吧,更精彩的還在后頭呢,繼續

編碼pair

這里簡單解釋下pair,其實就是序列對,如(1 2)、(hello world),這些就是pair,只有兩個元素,但不要小看了pair,我們用的list就是通過pair連接起來形成的。

Ask:我們能對pair做什么?

Answer:我們能夠選擇pair中的任意一個元素

好,知道了能對pair的操作,下面就用λ表達式來定義它:

mkpair x y = λb. (b x y)

fstp=p true

sndp=p false

這里用到了true與false的編碼。解釋一下:

pair就是這么一個函數,參數是一個boolean值,根據這個參數確定返回值。還是看例子:

fst (mkpair x y)--->(mkpair x y) true ---> true x y--->x

這樣我們就能取到pair的第一個元素了。很好玩吧,下面的更有趣,繼續

編碼number

這里講的number是指的自然數。

Ask:我們能對number做什么?

Answer:我們能夠依次遍歷這些數字

好,知道了能對number的操作,下面就用λ表達式來定義它:

0 = λf. λs. s

1 = λf. λs. f s

2 = λf. λs. f (f s)

......

解釋一下,利用currying,我們知道上面的定義其實相當于一個具有兩個參數的函數:一個函數f,另一個是起始值s,然后不斷應用f實現遍歷數字的操作。先不要管為什么這么定義,看了下面我們如何定義加法乘法的例子你應該就會豁然開朗了:

首先我們需要定義一個后繼函數(The successor function)

succn= λf. λs. f (n f s)

然后,就可以定義加法與乘法了

add n1 n2=n1 succ n2

mult n1 n2=n1 (add? n2) 0

只看定義要想弄懂應該還是有些困難,下面看個具體的例子:

add0=(λn1. λn2. n1 succ n2)0// 這里直接根據上面 add 定義進行展開

-------將 0 帶入 n1,可得-------->

λn2. 0 succ? n2= λn2. (λf.? λs.? s)? succ? n2

-------將 succ 帶入 f,n2帶入 s,可得-->

λn2. n2= λx. x

我第一次看這個例子有個疑問,add不是兩個參數嗎,你怎么就加一個0呢?其實還是currying沒理解好,兩個參數的函數內部不也是用一個參數的函數來表示的嘛,如果只傳遞一個參數,那么我們就知道還會返回一個函數,本例中就是λx. x,這是恒等函數,也就是說加 0 ,相當于什么也沒加,還是本身。

哈哈,看來也不過如此嘛,如果你能看到這里,說明你已經對lambda掌握的差不多了。下面再來看個“難點”的例子——1+1:

add 1 1 --->

1 succ 1 --->

succ 1 --->

λf. λs. f (f? s) ---> 2

最后一個例子,2*2:

mult 2 2 --->

2 (add 2)? 0 --->

(add 2) ((add? 2)? 0) --->

2 succ(add? 2? 0) --->

2 succ(2? succ? 0) --->

succ( succ? (succ? (succ? 0))) --->

succ( succ ( succ ( λf. λs. f (0 f s)))) --->

succ( succ ( succ ( λf. λs. f s))) --->

succ ( succ ( λg. λy. g (( λf. λs. f s) g y)))

succ( succ (λg. λy. g (g y))) --->......---> λg. λy. g (g (g (g y))) =4

不要一看到這么多步驟就嚇跑了,原則很簡單,就是不斷進行函數應用,需要注意的就是這里的2、0不再是單純的數字了,它是從具有兩個參數的函數,如果你應用時只傳入一個參數,說明它還會返回一個函數。

不管怎樣,如果你已經看到了這里,我希望你能把上面這個乘法的例子看懂,就是不斷進行函數應用而已,沒什么東西,我覺得難點在于思維的轉化,因為以前都很理所當然認為2×2=4了,而不知道這么簡單的計算后面的本質性東西,通過這個例子,希望大家能明確一點:值是什么不重要,重要的是我們能對這個值進行的操作

最后再來一個收尾菜:

如果想要判斷一個數字是否為0,可以這么定義

iszero? n = n ( λb.? false)? true

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容