在这个文件里我们来生成Y组合子,它是递归过程理论的一个基本结果。可能你已经知道,在某些情况下,是不需要给一个过程命名的。比如说:
((lambda (x) (+ x 1)) 6)
在没有名字的情况下,把1加到6上面去。但是,如果那是递归函数又该如何?比如说:
(define fact
(lambda (n)
(if (zero? n)
1
(* n (fact (- n 1))))))
它计算n的阶乘,看起来它需要名字fact,这样它才能够在过程的最后一行调用自己实现递归。但是,我们将会发现,其实它是不需要的,并且,在发现过程中还会养成许多使用Scheme的直觉。让我们一步一步来,每一步都轻微地改变一下"fact"。
第一步 第一个想法是像我们经常做的那样,简单地把fact当成一个参数传递进去。
(define op-maker
(lambda (op)
(lambda (x y)
(op x y))))
第一个lambda传递了操作的名字而第二个lambda是一个未命名的的操作。让我们用"fact"来试一下它。第一个尝试是
(define fact-maker
(lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n (procedure (- n 1)))))))
它的思想是通过"procedure"把"fact-maker"传递进去。因此,我们所要做的就是调用(fact-maker fact-maker)来产生我们的未命名(呃……应该是几乎未命名的)的阶乘函数。比如,这样写:
>((fact-maker fact-maker) 5)
120
但是,这不能运行,因为"fact-maker"接收一个过程作为参数,而"procedure",跟"fact"一样,需要一个数值型参数。解决办法是这样的:
(define fact-maker
(lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1)))))))
试一下它,比如说这样
>((fact-maker fact-maker) 5)
好了,我们把名字从过程体中除去了,但是我们仍然要通过名字传递一个过程进去。让我们试着把全部对名字的依赖除掉。
第二步 回想起我们需要"fact"让和(procedure procedure)一样,也就是和(fact-maker fact-maker)一样(回想上面的例子,((fact-maker fact-maker) 5)得出和(fact 5)一样的结果)。因此,利用第一步得到的结果,我们可以把"fact-maker"写成下面的样子:
(define fact
((lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1))))))
(lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1))))))))
用>(fact 5) 试一下它。
考虑下面的代码段:
(((lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1))))))
(lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1)))))))
5)
它产生了5的阶乘,因为被调用的过程(那一大堆代码)正好是"fact"的定义。但是,你瞧,过程里面已经没有任何名字了!
接下来,我们尝试把这个过程一般化,来结束我们那可怕但有用的Y组合子。
第三步 首先,我们要把属于计算阶乘的那一部分区别开来。目的是把这部分写在一个地方,当用其它问题的代码替代掉它以后,会生成一个新的递归过程。这一步有些技巧,我们依然给过程一个名字,因为我们不想作大的改动。我们从第二步得到的代码段是
(define F
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1))))))
因为它包含一个看得见的平凡的过程(procedure procedure),它跟我们想要的还有差距。所以我们用了一个小技巧来把它除去。一般地,
(f arg)
不就跟
((lambda (x) (f x)) arg) 一样吗?
尽管第二个语句有点奇怪,但是因为它让你把"arg"传递给一个要被应用到的过程,无论如何,过程被应用了。为什么我们要这样做?看!这意味着
((procedure procedure) (- n 1))
跟
((lambda (arg) ((procedure procedure) arg)) (- n 1))是一样的。
如果我们把这个替换到当前版本的F中,得到
(define F
(lambda (n)
(if (zero? n)
1
(* n ((lambda (arg) ((procedure procedure) arg)) (- n 1))))))
这有什么用?呃,(lambda (arg)...)是一个过程, 而procedure可以当成一个参数传递进去,因此,F可以定义成这样:
(define F
((lambda (func-arg)
(lambda (n)
(if (zero? n)
1
(* n (func-arg (- n 1))))))
(lambda (arg) ((procedure procedure) arg))))
没错,它就是那个F,但是旧的定义看起来像这样:
(define F (lambda (n) ... < procedure >))
而这新定义看起来像这样:
(define F ((lambda (func-arg) (lambda (n) ...)) < procedure >))
上式中,< procedure > 就是 (lambda (arg) ((procedure... ) ...) ...) 表达式。
((lambda (x) (+ x 1)) 6)
在没有名字的情况下,把1加到6上面去。但是,如果那是递归函数又该如何?比如说:
(define fact
(lambda (n)
(if (zero? n)
1
(* n (fact (- n 1))))))
它计算n的阶乘,看起来它需要名字fact,这样它才能够在过程的最后一行调用自己实现递归。但是,我们将会发现,其实它是不需要的,并且,在发现过程中还会养成许多使用Scheme的直觉。让我们一步一步来,每一步都轻微地改变一下"fact"。
第一步 第一个想法是像我们经常做的那样,简单地把fact当成一个参数传递进去。
(define op-maker
(lambda (op)
(lambda (x y)
(op x y))))
第一个lambda传递了操作的名字而第二个lambda是一个未命名的的操作。让我们用"fact"来试一下它。第一个尝试是
(define fact-maker
(lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n (procedure (- n 1)))))))
它的思想是通过"procedure"把"fact-maker"传递进去。因此,我们所要做的就是调用(fact-maker fact-maker)来产生我们的未命名(呃……应该是几乎未命名的)的阶乘函数。比如,这样写:
>((fact-maker fact-maker) 5)
120
但是,这不能运行,因为"fact-maker"接收一个过程作为参数,而"procedure",跟"fact"一样,需要一个数值型参数。解决办法是这样的:
(define fact-maker
(lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1)))))))
试一下它,比如说这样
>((fact-maker fact-maker) 5)
好了,我们把名字从过程体中除去了,但是我们仍然要通过名字传递一个过程进去。让我们试着把全部对名字的依赖除掉。
第二步 回想起我们需要"fact"让和(procedure procedure)一样,也就是和(fact-maker fact-maker)一样(回想上面的例子,((fact-maker fact-maker) 5)得出和(fact 5)一样的结果)。因此,利用第一步得到的结果,我们可以把"fact-maker"写成下面的样子:
(define fact
((lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1))))))
(lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1))))))))
用>(fact 5) 试一下它。
考虑下面的代码段:
(((lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1))))))
(lambda (procedure)
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1)))))))
5)
它产生了5的阶乘,因为被调用的过程(那一大堆代码)正好是"fact"的定义。但是,你瞧,过程里面已经没有任何名字了!
接下来,我们尝试把这个过程一般化,来结束我们那可怕但有用的Y组合子。
第三步 首先,我们要把属于计算阶乘的那一部分区别开来。目的是把这部分写在一个地方,当用其它问题的代码替代掉它以后,会生成一个新的递归过程。这一步有些技巧,我们依然给过程一个名字,因为我们不想作大的改动。我们从第二步得到的代码段是
(define F
(lambda (n)
(if (zero? n)
1
(* n ((procedure procedure) (- n 1))))))
因为它包含一个看得见的平凡的过程(procedure procedure),它跟我们想要的还有差距。所以我们用了一个小技巧来把它除去。一般地,
(f arg)
不就跟
((lambda (x) (f x)) arg) 一样吗?
尽管第二个语句有点奇怪,但是因为它让你把"arg"传递给一个要被应用到的过程,无论如何,过程被应用了。为什么我们要这样做?看!这意味着
((procedure procedure) (- n 1))
跟
((lambda (arg) ((procedure procedure) arg)) (- n 1))是一样的。
如果我们把这个替换到当前版本的F中,得到
(define F
(lambda (n)
(if (zero? n)
1
(* n ((lambda (arg) ((procedure procedure) arg)) (- n 1))))))
这有什么用?呃,(lambda (arg)...)是一个过程, 而procedure可以当成一个参数传递进去,因此,F可以定义成这样:
(define F
((lambda (func-arg)
(lambda (n)
(if (zero? n)
1
(* n (func-arg (- n 1))))))
(lambda (arg) ((procedure procedure) arg))))
没错,它就是那个F,但是旧的定义看起来像这样:
(define F (lambda (n) ... < procedure >))
而这新定义看起来像这样:
(define F ((lambda (func-arg) (lambda (n) ...)) < procedure >))
上式中,< procedure > 就是 (lambda (arg) ((procedure... ) ...) ...) 表达式。
第1楼
|