Recursion 在Scheme中,如何使用lambda创建递归函数?

Recursion 在Scheme中,如何使用lambda创建递归函数?,recursion,lambda,scheme,anonymous-recursion,Recursion,Lambda,Scheme,Anonymous Recursion,我在Scheme类中,我很好奇不使用define编写递归函数。当然,主要的问题是,如果函数本身没有名称,就无法调用函数本身 我确实找到了这个例子:它是一个只使用lambda的阶乘生成器 ((lambda (x) (x x)) (lambda (fact-gen) (lambda (n) (if (zero? n) 1 (* n ((fact-gen fact-gen) (sub1 n))))))) 但是我甚至不能理解第一个电话,(lambd

我在Scheme类中,我很好奇不使用define编写递归函数。当然,主要的问题是,如果函数本身没有名称,就无法调用函数本身

我确实找到了这个例子:它是一个只使用lambda的阶乘生成器

((lambda (x) (x x))
 (lambda (fact-gen)
   (lambda (n)
     (if (zero? n)
         1
         (* n ((fact-gen fact-gen) (sub1 n)))))))
但是我甚至不能理解第一个电话,(lambda(x)(x)):那到底是做什么的?在哪里输入想要得到阶乘的值

这不是针对类的,这只是出于好奇。

(lambda(x)(x))
获取函数对象,然后使用一个参数调用该对象,即函数对象本身

然后使用另一个函数调用该函数,该函数将该函数对象置于参数名
fact gen
下。它返回一个接受实际参数的lambda,
n
。这就是
((fact gen fact gen)(sub1 n))
的工作原理

如果你能理解的话,你应该阅读本书的样本章节(第9章)。它讨论了如何构建这种类型的函数,并最终将此模式提取到中(通常可用于提供递归)。

表达式
(lambda(x)(x))
创建一个函数,当使用一个参数(必须是函数)求值时,将该函数本身作为参数应用

给定表达式的计算结果是一个函数,该函数接受一个数值参数并返回该参数的阶乘。要尝试它:

(let ((factorial ((lambda (x) (x x))
                  (lambda (fact-gen)
                    (lambda (n)
                      (if (zero? n)
                          1
                          (* n ((fact-gen fact-gen) (sub1 n)))))))))
  (display (factorial 5)))
在您的示例中有几个层,值得一步一步地进行,并仔细检查每个层的作用。

(lambda(x)(x))
是一个在自身上调用参数x的函数

您发布的整个代码块产生一个参数函数。你可以这样称呼它:

(((lambda (x) (x x))
  (lambda (fact-gen)
    (lambda (n)
      (if (zero? n)
          1
          (* n ((fact-gen fact-gen) (sub1 n)))))))
 5)
(let ((fact #f)) 
  (set! fact 
        (lambda (n) (if (< n 2) 1 
                               (* n (fact (- n 1)))))) 
  (fact 5))
用5来调用它,返回120

在高层次上考虑这一点最简单的方法是,第一个函数,
(lambda(x)(x))
给x一个自身的引用,因此现在x可以引用自身,从而递归

我对不使用define编写递归函数感到好奇。 当然,主要的问题是,您不能在内部调用函数 如果它没有名字的话

这里有点离题,但是看到上面的陈述,我只是想让你知道“不使用define”并不意味着“没有名字”。可以给某个对象命名,并在Scheme中递归使用它,而无需定义

(letrec
  ((fact
     (lambda (n)
       (if (zero? n)
         1
         (* n (fact (sub1 n)))))))
  (fact 5))

如果你的问题特别提到“匿名递归”,那就更清楚了。

基本上你所拥有的是一种类似于Y组合符的形式。如果您重构了特定于阶乘的代码,以便实现任何递归函数,那么剩下的代码将是Y组合器

为了更好地理解,我自己已经完成了这些步骤。


如果你不喜欢我写的东西,就在谷歌上搜索一下Y Combinator(函数)。

我喜欢这个问题。”《scheme编程语言》是一本好书。我的想法来自那本书的第二章

首先,我们知道:

(letrec ((fact (lambda (n) (if (= n 1) 1 (* (fact (- n 1)) n))))) (fact 5))
使用
letrec
我们可以递归地生成函数。当我们调用
(fact 5)
时,我们看到,
fact
已经绑定到一个函数。如果我们有另一个函数,我们可以这样叫它
(另一个事实5)
,现在另一个
叫做二进制函数(对不起,我的英语不好)。我们可以将另一个
定义为:

(let ((another (lambda (f x) .... (f x) ...))) (another fact 5))
我们为什么不这样定义事实呢

(let ((fact (lambda (f n) (if (= n 1) 1 (* n (f f (- n 1))))))) (fact fact 5))
如果
fact
是一个二进制函数,那么可以使用函数
f
和整数
n
调用它,在这种情况下,函数
f
恰好是
fact
本身


如果您具备以上所有条件,现在就可以编写Ycombinator,将
替换为
let
lambda

您可以这样定义它:

(((lambda (x) (x x))
  (lambda (fact-gen)
    (lambda (n)
      (if (zero? n)
          1
          (* n ((fact-gen fact-gen) (sub1 n)))))))
 5)
(let ((fact #f)) 
  (set! fact 
        (lambda (n) (if (< n 2) 1 
                               (* n (fact (- n 1)))))) 
  (fact 5))
这里的微妙之处在于,由于
let
的作用域规则,lambda表达式不能引用正在定义的名称

当调用
((uh)5)
时,它将在
let
表单创建的环境框架内,简化为
((hh)5)
应用程序

现在,将
h
应用于
h
将创建新的环境框架,其中
g
指向其上方环境中的
h

(let ((U  (lambda (x) (x x)))
      (h  (lambda (g)
            (lambda (n)
              (if (zero? n)
                  1
                  (* n ((g g) (sub1 n))))))))
  ( (let ((g h))
      (lambda (n)
              (if (zero? n)
                  1
                  (* n ((g g) (sub1 n)))))) 
    5))
这里的
(lambda(n)…
表达式是从环境框架内部返回的,其中
g
指向它上面的
h
,作为。即一个参数的函数,
n
,它还记住了
g
h
U
的绑定

因此,当调用此闭包时,
n
将被分配
5
,并输入
if
表单:

(let ((U  (lambda (x) (x x)))
      (h  (lambda (g)
            (lambda (n)
              (if (zero? n)
                  1
                  (* n ((g g) (sub1 n))))))))
    (let ((g h))
      (let ((n 5))
              (if (zero? n)
                  1
                  (* n ((g g) (sub1 n)))))))
(g)
应用程序被缩减为
(h)
应用程序,因为
g
指向创建闭包对象的环境上方的环境框架中定义的
h
。也就是说,在上面,在顶部的
let
表单中。但是我们已经看到了
(hh)
调用的减少,它创建了闭包,即一个参数
n
的函数,作为我们的
阶乘
函数,在下一次迭代中将使用
4
调用它,然后使用
3
等等


它是一个新的闭包对象,还是重复使用同一个闭包对象,取决于编译器。这可能会影响性能,但不会影响递归的语义。

我发现这个问题是因为我需要宏中的递归助手函数,而宏中不能使用define

有人想了解
(lambda(x)(x x))
和Y-combinator,但名为let的人在不吓跑游客的情况下完成了任务: