在第二种情况下无法理解输出

问题描述 投票:1回答:2
(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,为什么会这样呢?

racket
2个回答
1
投票

你在想这个:

(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语法使用生成的闭包值创建一个全局变量c1let超出了范围,但是d没有收集垃圾,因为它仍然被一个值(闭包)引用。


0
投票

let可以被重写为lambda抽象的直接应用

(mylet ([var rhs] ...) body ...) => ((lambda (var ...) body ...) rhs ...)

脱糖clet产量

(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)

88。 (这是你的预期)。但实际上,正在发生的事情是

(define c1
  (f1 10))
(c1 2)
(c1 2)

返回86

d得到记忆(here是使用memoize程序和set!相同包装的斐波纳契的一个例子)。

而且,对于set!,你不能有天真的替代。球拍的evaluation model解释了“为每个应用程序的每个变量创建一个新位置”:

由于可以更改与参数变量x关联的值,因此在首次应用过程时,该值不能替换为x。

tl; dr对c1的评估产生一个lambda,它关闭等待替换(环境)。然后在每次通话时由set!进行变异。

© www.soinside.com 2019 - 2024. All rights reserved.