Y组合子工程推导全过程!

缘起

第一次听说Y组合子,大概是在19年的时候。当时看到这么个东西的时候,就觉得很漂亮。然后,也不知道薅掉多少根头发,终于在最近顿悟了其中的关键步骤,遂把思路整理成文章记录下来。

先简单说一下Y组合子产生的背景吧。

上个世纪三十年代,丘奇发明了Lambda演算(它是后来很多函数式变成的理论基础)。大概是因为信奉如无必要勿增实体,当年老爷子提出的理论里,函数是单参的、也没有名字的概念。

之后,柯里先救了一次场,证明多参函数可以用单参函数等价表示,这就是函数式编程里大名鼎鼎的柯里化。

但是没有函数名,怎么实现递归呢?关键时刻,柯里大神再次救场,证明了不需要名字,也能实现函数的递归。

不过,纯数学的理论证明咱也不会啊,所以,下面就用Scheme语言(基于Lambda的一种Lisp方言)以工程的视角推导出一个Y组合子。

推导过程

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

这是一个递归求阶乘的函数。刚才说了,Lambda演算不能存在函数名,也就是说不能用define定义fact。但是,这里其实有一个变通方案:不能定义函数名,但是可以给变量命名,比如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 (...)),其中...的部分就是第一个式子的内容,即((some-name 'null) 1)

  • 针对第三个式子,n = 3时,带入得到 (* 3 (...)),其中...的部分就是第二个式子的内容,即((some-name (some-name 'null)) 2)

其实可以看出来,只要最后的(fact 0)不要真的执行到,就可以算越来越大的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已经没有关系了,完全可以把some-name用它真正的定义塞进去

(
  ; 这其实是一个函数,接受一个参数n,计算n的阶乘
  (
    (lambda (g) (g g))
    ; 这其实是刚才的some-name
    (lambda (fact)
      (lambda (n)
        (if (< n 2) 1 (* n ((fact fact) (- n 1))))))

  )
4)

上面的那个函数成为“穷人的Y组合子”,因为它指针对特定的递归函数生效,在这个例子里是fact

我们希望把fact提出去,让这个函数更通用一些

首先把fact代换为f(这一步实际上只是为了好看)

(
  ; 这其实是一个函数,接受一个参数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)
      (
        ; 这是最初的阶乘函数
        (lambda (fact) 
          (lambda (n)
            (if (< n 2) 1 (* n (fact (- n 1))))))
        ; 用lambda包一下,本质上就是刚才的 (f f)
        ; 这一步主要是因为Scheme是应用序求值
        ; 因此,如果不用lambda让它延迟求值的话,就会提前递归下去
        (lambda (x) ((f f) x))
      )
    )
  )
4)

再把fact相关的提出去

(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组合子

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

拿阶乘函数先测试下

((Y some-name) 5)

试试斐波那契数列

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

((Y meta-fib) 10)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容