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
不会被垃圾回收,因为它仍然由一个值(闭包)引用 Alet
可以重写为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对其进行变异每次通话时使用code>。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)