为什么这个letrec的初始化形式的变量是undefined?

Why is the variable undefined in the initialization form of this letrec?

方案抱怨在下面的代码中,(+ x 1) 中的 x 未定义:

(letrec ((x 1)
         (y (+ x 1)))
  y)

所以,我又定义了一个x:

(let ((x 999))
  (letrec ((x 1)
           (y (+ x 1)))
    y))

然而,Scheme 实现仍然抱怨 (+ x 1) 中的 x 未定义。到底是怎么回事?很明显,我已经定义了x.

那不是 letrec 的工作方式。你可以认为 letrec 好像它做了一些接近于此的事情(例如参见 [​​=36=], 4.2.2)。

(letrec ((a <xpr>)
         ...inits)
  ...body)

相当于

(let ((a <illegal-value>)
      ...)
  (set! a <xpr>)
  ...set other variables...
  ...body)

其中 <illegal-value> 是您永远不会引用的东西,哪些实现可能会使引用变得非法。

这不完全是 letrec 所做的,因为在上面的扩展中,赋值是按定义的顺序发生的,而在 letrec 中,初始化形式的评估不是。因此,更高级的扩展可能首先使用 let,因为它的求值顺序也是 non-defined:

(let ((a <illegal-value>)
      ...)
  (let ((<t1> <xpr>)
        ...)
    (set! a <t1>)
    ...set other variables...
    ...body))

现在我们可以看到

(let ((a 999))
  (letrec ((a 1)
           (b a))
    b))

会失败,因为扩展会插入 另一个 let,它 shadows 外部绑定:

(let ((a 999))
  (let ((a <illegal>)
        (b <illegal>))
    (let ((t1 1)
          (t2 a))
      (set! a t1)
      (set! b t2)
      b)))

你可以看到当 t2 绑定到 a 时出现问题,它指的是 a 绑定的当前值 <illegal>,而外层a的值为999是不可访问的。

另一方面,这个:

(letrec ((a (lambda () b))
         (b (lambda () a)))
  (eq? ((b)) b))

两者都有效,并且 return 为真,因为在扩展中 (b) 将在 之后 set! 被调用。

letrec最常见的用途是它允许绑定递归函数:在类似

的地方
(letrec ((f (lambda (n)
              (if (<= n 2)
                  n
                  (+ (f (- n 1)) 1)))))
  ...)

然后 f 需要引用 f 相同的绑定 才能工作,但它实际上并没有查看那个值绑定直到函数被调用,届时一切正常。

但是,请参阅 my other answer 了解 letrec 的一些其他可能用途。