应用序 Y 组合子 的推导过程

虽说Dan Friedman 大大的《The Little Schemer》确实是通俗易懂,跟一科普读物一样,照顾我们这些小白,但是看到第九章讲Y组合子还是费了一番功夫的,不知道是我笨还是 Y 确实是个不好理解的东西,反正各位看看就行,我就是相当于复习一下功课,让自己能够跟的上思路,写的不好就喷吧,我也不怕,反正菜。


正文开始,说起Y组合子这个东西,我知道的有两种,应用序和正则序,区别就是参数求值的时机不一样

1.应用序是先对参数求值再pass给function

2.正则序则不是这样,正则序又有一个别名,叫lazy evalution,就是说一个表达式能不求值就不去求值它,除非到了要用到它的值了的时候

这篇文章讨论的是应用序,关于正则序,网上文章一大堆,我也是只能理解个大概(笨得吃翔)。不废话了,现有一个累加器:

(define acc
  (lambda (n)
    (cond ((= n 0) 0)
         (else (+ n (acc (- n 1)))))))

 
现在这个累加器实现了一个线性递归,通过自己调用自己。因为define 把 acc 绑定到了这个lambda表达式上,所以我们才可以调用它自身,但要是没有define这种东西呢?我们又该如何实现一个递归呢? 毕竟lambda calculi 就是没有define这种东西的
</pre><span style="white-space:pre"></span><pre name="code" class="plain">(lambda (n)
    (cond ((= n 0) 0)
         (else (+ n (? (- n 1))))))

是吧?没有acc我们如何调用自身,让我们修改一下这个lambda,接受一个另外的参数--递归函数


(lambda (acc) 
  (lambda (n)
    (cond ((= n 0) 0)
         (else (+ n (acc (- n 1)))))))

如果我们把它自身传给它,是不是就是实现了递归? 我们试试看:

(lambda (acc)
  ((acc acc))
  (lambda (acc) 
    (lambda (n)
      (cond ((= n 0) 0)
           (else (+ n (acc (- n 1))))))))

很明显第一个lambda接受一个参数,然后把自身应用到自身,我们看看,这个参数就是第二个lambda 我们 把应用过程画下来:


(lambda (acc)
  (lambda (n)
    (cond ((= n 0) 0)
         (else (+ n (lambda (acc)
                      (lambda (n)
                        (cond ((= n 0) 0)
                             (else (+ n (acc (- n 1))))))
                      (- n 1)))))))
 

有个问题不知道你注意到没有,就是累加函数(第二个lambda)接受一个参数,返回另一个函数,所以在 (acc (- n 1))这里就有问题了,它返回了一个函数,并把(- n 1)当作一个函数再次应用,明显不行。所以我们得再想办法。如果我们再给acc喂一个acc呢?

(lambda (acc)
  ((acc acc))
  (lambda (acc) 
    (lambda (n)
      (cond ((= n 0) 0)
           (else (+ n ((acc acc) (- n 1))))))))

 

这样(acc acc)返回的就是一个接受n的函数了,我们刚好传递(- n 1)给它,但是貌似又出问题了,这个函数貌似没有结束的一天。。。因为acc不断应用到acc,不断应用。。囧,不管了,我们先把函数调整一下,acc 是累加器,而我们还得制造累加器,所以:

(lambda (mk-acc)
  ((mk-acc mk-acc))
  (lambda (mk-acc)
    (lambda (acc) 
     (lambda (n)
      (cond ((= n 0) 0)
           (else (+ n (acc (- n 1)))))))
      (mk-acc mk-acc))))


注意我把累加器分离出来了,就是从那个lambda (n)开始的。但问题没变,(mk-acc mk-acc) 返回的是一个接受参数返回一个函数的函数,所以老规矩,再喂一个x给它:
<span style="font-family: Arial, Helvetica, sans-serif;">(lambda (mk-acc)</span>
  ((mk-acc mk-acc))
  (lambda (mk-acc)
    (lambda (acc) 
     (lambda (n)
      (cond ((= n 0) 0)
           (else (+ n (acc (- n 1)))))))
        (lambda (x)
	  ((mk-acc mk-acc) x))))
</pre><pre name="code" class="plain" style="color: rgb(204, 0, 0); line-height: 20.020000457763672px;">这下mk-acc总能返回正确的累加器了吧,好,最后一步,为了得到普遍使用的Y,我们要把累加器彻底分离出来:
(lambda (acc)
  ((lambda (mk-acc)
    (mk-acc mk-acc))
   (lambda (mk-acc)
     (acc (lambda (x)
            ((mk-acc mk-acc) x)))))
  (lambda (acc)
    (cond ((= n 0) 0)
         (else (+ 1 (acc (- n 1)))))))

前半部分就是我们的application-order Y conmbinator了:

 
(define Y
  (lambda (f)
    (lambda (mf) (mf mf))
    (lambda (mf)
      (f (lambda (x) ((mf mf) x)))))))

呼,自己知道写的烂,好歹一个字一个字码出来的,看官们将就着看把。
 
 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值