为什么这个letrec的初始化形式的变量是undefined?
Why is the variable undefined in the initialization form of this letrec?
方案抱怨在下面的代码中,(+ x 1)
中的 x
未定义:
(letrec ((x 1)
(y (+ x 1)))
y)
- Chez 方案:
Exception: attempt to reference undefined variable x
- MIT 方案:
;Unassigned variable: x
所以,我又定义了一个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
的一些其他可能用途。
方案抱怨在下面的代码中,(+ x 1)
中的 x
未定义:
(letrec ((x 1)
(y (+ x 1)))
y)
- Chez 方案:
Exception: attempt to reference undefined variable x
- MIT 方案:
;Unassigned variable: x
所以,我又定义了一个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
的一些其他可能用途。