首页 > 其他 > 详细

简单易懂的程序语言入门小册子(4):基于文本替换的解释器,递归,如何构造递归函数,Y组合子

时间:2014-04-21 12:48:35      阅读:660      评论:0      收藏:0      [点我收藏+]

递归。哦,递归。 递归在计算机科学中的重要性不言而喻。 递归就像女人,即令人烦恼,又无法抛弃。

先上个例子,这个例子里的函数double输入一个非负整数nbubuko.com,布布扣 ,输出2nbubuko.com,布布扣

double=λn.(if(iszeron)0(+2(double(?n1))))bubuko.com,布布扣

现在的问题是,这个递归函数在我们的语言里没法直接定义。 我说的直接定义是指像这个用let表达式:

(letdoubleλn.(if(iszeron)0(+2(double(?n1))))M)bubuko.com,布布扣
把这个let表达式宏展开会看得更清楚些:
(λdouble.Mλn.(if(iszeron)0(+2(double(?n1)))))bubuko.com,布布扣
λn.(if(iszeron)0(+2(double(?n1))))bubuko.com,布布扣 里的double是个自由变量。 解释器求值到这里的时候,根本不知道double指的是什么函数。

如何构造递归函数

获得递归的一个关键是如何在函数体中找到自己(结合一开始的比喻,这句话好像蕴含了其他意义深远的意思)。 一个简单的方法是在double上增加一个参数(一般就是第一个参数),把自己传入参数。 把这个修改后的函数叫做mkdouble1吧。 先不考虑mkdouble1的定义,先观察mkdouble1的行为。 因为调用mkdouble1要把自己作为第一个参数传入,所以调用递归函数应该这样写:

((mkdouble1mkdouble1)n)bubuko.com,布布扣
也就是说,double就是(mkdouble1mkdouble1)bubuko.com,布布扣
  doublebubuko.com,布布扣              bubuko.com,布布扣bubuko.com,布布扣=bubuko.com,布布扣=bubuko.com,布布扣bubuko.com,布布扣(mkdouble1mkdouble1)bubuko.com,布布扣(λv.(vv)mkdouble1)bubuko.com,布布扣bubuko.com,布布扣bubuko.com,布布扣
最后一步变换是为了让mkdouble1只出现一次。

现在来考虑mkdouble1的定义。 在double上增加一个参数fbubuko.com,布布扣

mkdouble1=λf.λn.(if(iszeron)0(+2(double(?n1))))bubuko.com,布布扣
函数调用的时候传入参数fbubuko.com,布布扣 的是mkdouble1。 也就是说fbubuko.com,布布扣 代表的是mkdouble1。 因此,函数体里递归调用的double用(ff)bubuko.com,布布扣 替换:
mkdouble1=λf.λn.(if(iszeron)0(+2((ff)(?n1))))bubuko.com,布布扣
所以double的定义是:
double=(λv.(vv)λf.λn.(if(iszeron)0(+2((ff)(?n1)))))bubuko.com,布布扣
这个定义可以用之前实现的解释器运行。 测试一下:

1
2
3
4
5
6
7
‘(let double ((lambda v (v v))
              (lambda f
                (lambda n
                  (if (iszero n) 0 (+ 2 ((f f) (- n 1)))))))
   (double 4))
 
>> 8

Y组合子

这一小节比较理论,知道个思路就行了。所以我就随便写写。 好学的人可以自己查资料(Programming Languages and Lambda Calculi, The Little Schemer)。

mkdouble1并不能让人很满意,因为它不优雅(都是时臣的错)。 mkdouble1递归调用的地方用的是(ff)bubuko.com,布布扣 ,而比较好看比较符合直觉的应该只有一个fbubuko.com,布布扣 。 定义这个所谓的比较好看的函数mkdouble如下:

mkdouble=λf.λn.(if(iszeron)0(+2(f(?n1))))bubuko.com,布布扣
我们希望能从mkdouble得到递归函数double。 这是能做到的。只要在利用mkdouble1的double定义上做几个简单的推导就行了:
doublebubuko.com,布布扣bubuko.com,布布扣bubuko.com,布布扣bubuko.com,布布扣bubuko.com,布布扣=bubuko.com,布布扣=bubuko.com,布布扣=bubuko.com,布布扣=bubuko.com,布布扣bubuko.com,布布扣(λv.(vv)λf.λn.(if(iszeron)0(+2((ff)(?n1)))))bubuko.com,布布扣(λv.(vv)λf.(λf.λn.(if(iszeron)0(+2(f(?n1))))(ff)))bubuko.com,布布扣(λx.(λv.(vv)λf.(x(ff)))λf.λn.(if(iszeron)0(+2(f(?n1)))))bubuko.com,布布扣(λx.(λv.(vv)λf.(x(ff)))mkdouble)bubuko.com,布布扣bubuko.com,布布扣bubuko.com,布布扣

λx.(λv.(vv)λf.(x(ff)))bubuko.com,布布扣 被称作Y组合子,记为Y。 然后有:

double=(Ymkdouble)bubuko.com,布布扣

Y组合子可以用来构造递归函数。 不过上面的定义在call-by-value的调用方式下会进入无限循环。 具体原因就不讲了,只讲结论:问题出在(ff)bubuko.com,布布扣 这里,对(ff)bubuko.com,布布扣 做一个ηbubuko.com,布布扣 逆归约就行了。 修改后的Y组合子记为Ybubuko.com,布布扣vbubuko.com,布布扣bubuko.com,布布扣

Ybubuko.com,布布扣vbubuko.com,布布扣=λx.(λv.(vv)λf.(x(λu.((ff)u))bubuko.com,布布扣

测试一下。 Call-by-value的测试:

1
2
3
4
5
6
7
8
9
10
11
‘(let Y (lambda x
          ((lambda v (v v))
           (lambda f (x (lambda u ((f f) u))))))
   (let mkdouble (lambda f
                   (lambda n
                     (if (iszero n)
                         0
                         (+ 2 (f (- n 1))))))
     ((Y mkdouble) 4)))
 
>> 8

Call-by-name的测试:

1
2
3
4
5
6
7
8
9
10
11
‘(let Y (lambda x
          ((lambda v (v v))
           (lambda f (x (f f)))))
   (let mkdouble (lambda f
                   (lambda n
                     (if (iszero n)
                         0
                         (+ 2 (f (- n 1))))))
     ((Y mkdouble) 4)))
 
>> 8

简单易懂的程序语言入门小册子(4):基于文本替换的解释器,递归,如何构造递归函数,Y组合子,布布扣,bubuko.com

简单易懂的程序语言入门小册子(4):基于文本替换的解释器,递归,如何构造递归函数,Y组合子

原文:http://www.cnblogs.com/skabyy/p/3677836.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!