(define c
(let ((d 10))
(set! d (- d 2))
d))
(define c1
(let ((d 10))
(lambda (p)
(set! d (- d p))
d)))
现在为c c
输出是8 8
。
但对于(c1 2) (c1 2)
,输出是8 6
。为什么会这样?
我想我需要深入了解如何实际评估函数调用。
据我所知,评估应该是(在第二种情况下)第一次调用时,创建函数c1的局部环境,其中d的值为10,然后程序评估正常进行。然后,一旦该呼叫结束,整个环境就被破坏,并且对于第二次呼叫,发生相同的整个过程(如上所述)。所以第二个输出值也应该是8.但它是6,为什么会这样呢?
你在想这个:
(define c1
(let ((d 10))
(lambda (p)
(set! d (- d p))
d)))
它完全相同:
(define c1
(lambda (p)
(let ((d 10))
(set! d (- d p))
d)))
它不是。在第一个变量d
是在lambda之前创建的,因此它对于c1
的每次调用都是相同的自由变量。因此,改变d
会改变下一个电话。
第二个在调用时创建d
,并在调用完成时被销毁。
在第一个Scheme中评估let表单。它创建d
然后计算lambda,因此d
成为其返回的闭包中的自由变量。然后,define语法使用生成的闭包值创建一个全局变量c1
。 let
超出了范围,但是d
没有收集垃圾,因为它仍然被一个值(闭包)引用。
let
可以被重写为lambda
抽象的直接应用
(mylet ([var rhs] ...) body ...) => ((lambda (var ...) body ...) rhs ...)
脱糖c
的let
产量
(define c ((lambda (d) (set! d (- d 2)) d) 10))
这只是10
在函数上的应用(我们称之为f
)
(define f (lambda (d) (set! d (- d 2)) d))
(define c
(f 10))
c
c
至于c1
,我们有嵌套的lambdas
(define f1 (lambda (d) (lambda (p) (set! d (- d p)) d)))
((f1 10) 2)
((f1 10) 2)
8
和8
。 (这是你的预期)。但实际上,正在发生的事情是
(define c1
(f1 10))
(c1 2)
(c1 2)
返回8
和6
d得到记忆(here是使用memoize程序和set!
相同包装的斐波纳契的一个例子)。
而且,对于set!
,你不能有天真的替代。球拍的evaluation model解释了“为每个应用程序的每个变量创建一个新位置”:
由于可以更改与参数变量x关联的值,因此在首次应用过程时,该值不能替换为x。
tl; dr对c1
的评估产生一个lambda,它关闭等待替换(环境)。然后在每次通话时由set!
进行变异。