Y組合子工程推導全過程萧福!

緣起

第一次聽說Y組合子拉鹃,大概是在19年的時候。當時看到這么個東西的時候鲫忍,就覺得很漂亮毛俏。然后,也不知道薅掉多少根頭發(fā)饲窿,終于在最近頓悟了其中的關(guān)鍵步驟煌寇,遂把思路整理成文章記錄下來。

先簡單說一下Y組合子產(chǎn)生的背景吧逾雄。

上個世紀三十年代阀溶,丘奇發(fā)明了Lambda演算(它是后來很多函數(shù)式變成的理論基礎(chǔ))腻脏。大概是因為信奉如無必要勿增實體,當年老爺子提出的理論里银锻,函數(shù)是單參的永品、也沒有名字的概念。

之后击纬,柯里先救了一次場鼎姐,證明多參函數(shù)可以用單參函數(shù)等價表示,這就是函數(shù)式編程里大名鼎鼎的柯里化更振。

但是沒有函數(shù)名炕桨,怎么實現(xiàn)遞歸呢?關(guān)鍵時刻肯腕,柯里大神再次救場献宫,證明了不需要名字,也能實現(xiàn)函數(shù)的遞歸实撒。

不過姊途,純數(shù)學的理論證明咱也不會啊,所以知态,下面就用Scheme語言(基于Lambda的一種Lisp方言)以工程的視角推導出一個Y組合子捷兰。

推導過程

(define fact
  (lambda (n)
    (if (< n 2) 1 (* n (fact (- n 1)))))
)

這是一個遞歸求階乘的函數(shù)。剛才說了负敏,Lambda演算不能存在函數(shù)名贡茅,也就是說不能用define定義fact。但是原在,這里其實有一個變通方案:不能定義函數(shù)名友扰,但是可以給變量命名,比如n庶柿。所以村怪,第一步,我們把fact作為變量傳進來浮庐。

(define some-name
  (lambda (fact)
    (lambda (n)
      (if (< n 2) 1 (* n (fact (- n 1))))))
)
((some-name 'null) 1)
((some-name (some-name 'null)) 2)
((some-name (some-name (some-name 'null))) 3)
  • 針對第一個式子甚负,其實fact傳入的是什么東西都無所謂,因為n = 1审残,所以不會走到(fact 0)梭域,否則fact帶入'null,會報錯

  • 針對第二個式子搅轿,n = 2時病涨,帶入得到 (* 2 (...)),其中...的部分就是第一個式子的內(nèi)容璧坟,即((some-name 'null) 1)

  • 針對第三個式子既穆,n = 3時赎懦,帶入得到 (* 3 (...)),其中...的部分就是第二個式子的內(nèi)容幻工,即((some-name (some-name 'null)) 2)

其實可以看出來励两,只要最后的(fact 0)不要真的執(zhí)行到,就可以算越來越大的n

但是囊颅,我們不想 n = 4式当悔,寫這么長的式子了,((some-name (some-name (some-name (some-name 'null)))) 4)

所以我們試著做以下這2個替換:

第一步:既然'null都可以讓程序跑起來踢代,那替換成some-name是不是也可以盲憎?

((some-name some-name) 1)
((some-name (some-name some-name)) 2)
((some-name (some-name (some-name some-name))) 3)

第二步:既然程序最后終止在(fact 0),用some-name帶入fact后奸鬓,實際上是終止在(some-name 0)

那是不是把(fact 0)改寫成((fact fact) 0)焙畔,程序就不會終止了掸读?

(define some-name
  (lambda (fact)
    (lambda (n)
      (if (< n 2) 1 (* n ((fact fact) (- n 1))))))
)
((some-name some-name) 1)

((some-name some-name) 2)
; = (* 2 ((some-name some-name) 1))

((some-name some-name) 3)
; = (* 3 ((some-name some-name) 2))
; = (* 3 (* 2 ((some-name some-name) 1)))

((some-name some-name) 4)
; = (* 4 ((some-name some-name) 3))
; = (* 4 (* 3 ((some-name some-name) 2)))
; = (* 4 (* 3 (* 2 ((some-name some-name) 1))))
(((lambda (g) (g g)) some-name) 4)

可以看到這時候串远,其實要不要some-name已經(jīng)沒有關(guān)系了,完全可以把some-name用它真正的定義塞進去

(
  ; 這其實是一個函數(shù)儿惫,接受一個參數(shù)n澡罚,計算n的階乘
  (
    (lambda (g) (g g))
    ; 這其實是剛才的some-name
    (lambda (fact)
      (lambda (n)
        (if (< n 2) 1 (* n ((fact fact) (- n 1))))))

  )
4)

上面的那個函數(shù)成為“窮人的Y組合子”,因為它指針對特定的遞歸函數(shù)生效肾请,在這個例子里是fact

我們希望把fact提出去留搔,讓這個函數(shù)更通用一些

首先把fact代換為f(這一步實際上只是為了好看)

(
  ; 這其實是一個函數(shù),接受一個參數(shù)n铛铁,計算n的階乘
  (
    (lambda (g) (g g))
    ; 這其實是剛才的some-name
    (lambda (f)
      (lambda (n)
        (if (< n 2) 1 (* n ((f f) (- n 1))))))

  )
4)

接著我們把(f f)提出去

(
  (
    (lambda (g) (g g))
    
    (lambda (f)
      (
        ; 這是最初的階乘函數(shù)
        (lambda (fact) 
          (lambda (n)
            (if (< n 2) 1 (* n (fact (- n 1))))))
        ; 用lambda包一下隔显,本質(zhì)上就是剛才的 (f f)
        ; 這一步主要是因為Scheme是應(yīng)用序求值
        ; 因此,如果不用lambda讓它延遲求值的話饵逐,就會提前遞歸下去
        (lambda (x) ((f f) x))
      )
    )
  )
4)

再把fact相關(guān)的提出去

(define some-name
  (lambda (fact)
    (lambda (n)
      (if (< n 2) 1 (* n (fact (- n 1))))))
)

(
  (
    (lambda (g) (g g))
    (lambda (f) (some-name (lambda (x) ((f f) x))))
  )
4)
(
  (
    ; This is Y !!!
    (lambda (fn)
      (
        (lambda (g) (g g))
        (lambda (f) (fn (lambda (x) ((f f) x))))
      )
    )
    some-name
  )
4)

最終我們得到Y(jié)組合子

(define Y
  (lambda (fn)
    ((lambda (g) (g g))
    (lambda (f) (fn (lambda (x) ((f f) x)))))))

拿階乘函數(shù)先測試下

((Y some-name) 5)

試試斐波那契數(shù)列

(define meta-fib
  (lambda (fib)
    (lambda (n)
      (if (< n 3) 1 (+ (fib (- n 1)) (fib (- n 2)))))))

((Y meta-fib) 10)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末括眠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子倍权,更是在濱河造成了極大的恐慌掷豺,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薄声,死亡現(xiàn)場離奇詭異当船,居然都是意外死亡,警方通過查閱死者的電腦和手機默辨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門德频,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缩幸,你說我怎么就攤上這事壹置〉凳澹” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵蒸绩,是天一觀的道長衙四。 經(jīng)常有香客問我,道長患亿,這世上最難降的妖魔是什么传蹈? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮步藕,結(jié)果婚禮上惦界,老公的妹妹穿的比我還像新娘。我一直安慰自己咙冗,他們只是感情好沾歪,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著雾消,像睡著了一般灾搏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上立润,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天狂窑,我揣著相機與錄音,去河邊找鬼桑腮。 笑死泉哈,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的破讨。 我是一名探鬼主播丛晦,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼提陶!你這毒婦竟也來了烫沙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤搁骑,失蹤者是張志新(化名)和其女友劉穎斧吐,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體仲器,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡煤率,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了乏冀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蝶糯。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖辆沦,靈堂內(nèi)的尸體忽然破棺而出昼捍,到底是詐尸還是另有隱情识虚,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布妒茬,位于F島的核電站担锤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏乍钻。R本人自食惡果不足惜肛循,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望银择。 院中可真熱鬧多糠,春花似錦、人聲如沸浩考。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽析孽。三九已至搭伤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绿淋,已是汗流浹背闷畸。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工尝盼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吞滞,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓盾沫,卻偏偏與公主長得像裁赠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子赴精,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容