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

Racket 无法理解第二种情况下的输出,racket,Racket,现在对于cc来说,输出是8 但是对于(c12)(c12)来说,输出是86。为什么会这样 我想我需要了解函数调用实际是如何计算的 根据我的说法,评估应该是,(在第二种情况下)对于第一次调用,为函数c1创建一个本地环境,其中d的值为10,然后程序评估正常进行。然后,一旦这个调用结束,整个环境就会被破坏,对于第二个调用,同样的整个过程(如上所述)也会发生。所以第二个输出值也应该是8。但它是6,为什么会这样?你是这样想的: (define c (let ((d 10)) (set! d (-

现在对于
cc
来说,输出是
8

但是对于
(c12)(c12)
来说,输出是
86
。为什么会这样

我想我需要了解函数调用实际是如何计算的

根据我的说法,评估应该是,(在第二种情况下)对于第一次调用,为函数c1创建一个本地环境,其中d的值为10,然后程序评估正常进行。然后,一旦这个调用结束,整个环境就会被破坏,对于第二个调用,同样的整个过程(如上所述)也会发生。所以第二个输出值也应该是8。但它是6,为什么会这样?

你是这样想的:

(define c
  (let ((d 10))
    (set! d (- d 2))
    d))
(define c1
  (let ((d 10))
   (lambda (p)
     (set! d (- d p))
     d)))
这与:

(define c1
  (let ((d 10))
    (lambda (p)
      (set! d (- d p))
      d)))
事实并非如此。在第一种情况下,变量
d
在lambda之前创建,因此每次调用
c1
时,它都是相同的自由变量。因此,更改
d
会改变下一个调用

第二个函数在调用时创建
d
,在调用完成时将被销毁


在第一个方案中,计算let形式。它创建
d
,然后计算lambda,使
d
在返回的闭包中成为自由变量。define语法随后创建一个全局变量
c1
,其中包含最终的闭包值。
let
超出范围,但是
d
不会被垃圾回收,因为它仍然由一个值(闭包)引用

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

(define c1
  (lambda (p)
    (let ((d 10))
      (set! d (- d p))
      d)))
脱糖
c
let
产量

(mylet ([var rhs] ...) body ...) => ((lambda (var ...) body ...) rhs ...)
这只是将
10
应用到函数上(我们称之为
f

至于
c1
,我们有嵌套的lambda

(define f (lambda (d) (set! d (- d 2)) d))
(define c 
  (f 10))
c
c
8
8
(这是您所期望的)。但实际上,发生的是

(define f1 (lambda (d) (lambda (p) (set! d (- d p)) d)))
((f1 10) 2)
((f1 10) 2)
返回
8
6

d被记忆化(是斐波那契的一个例子,它使用了与记忆化过程相同的包装和
set!

此外,对于
set,您不能进行原始替换。racket解释了“如何为每个应用程序上的每个变量创建新位置”:

由于与参数变量x关联的值可以更改, 第一次执行程序时,该值不能替换为x 应用


tl;dr
c1
的评估产生一个lambda,该lambda结束未决替代(环境)。然后通过
set对其进行变异

c
是函数还是数字?对我来说,评估
(C2)
会产生一个“应用程序:不是过程”错误。对不起,请查看@Alex Knauth对
c
第一个案例的编辑,即
集合表达式计算一次。对于第二种情况
c1
,设置
表达式被计算多次,每次调用函数一次。@Alex Knauth我确信在这两种情况下都设置了!计算2次当初始化
c
时,只计算一次。从那时起,
c
的值是
8
,多次引用
c
不会重新评估这一点。我认为您不应该使用此处记忆的术语。它特别意味着记住函数的结果,这样以后就不必重新计算它们了。也许你的意思更接近于“结束”,只是使用了错误的术语。是的,这是真的,在这种情况下,“记忆化”并不是最准确的术语。谢谢
(define f1 (lambda (d) (lambda (p) (set! d (- d p)) d)))
((f1 10) 2)
((f1 10) 2)
(define c1
  (f1 10))
(c1 2)
(c1 2)