Scheme 为什么方案允许在封闭环境中对封闭环境进行变异?
以下是方案代码:Scheme 为什么方案允许在封闭环境中对封闭环境进行变异?,scheme,Scheme,以下是方案代码: (let ((x 1)) (define (f y) (+ x y)) (set! x 2) (f 3) ) 它的计算结果是5而不是4。考虑到Scheme促进了静态作用域,这是令人惊讶的。在闭包中允许后续的变异影响封闭环境中的绑定似乎恢复到某种动态范围。有什么具体的理由允许这样做吗 编辑: 我意识到上面的代码不太明显,无法揭示我所关心的问题。我将另一段代码放在下面: (define x 1) (define (f y) (+ x y)) (set! x
(let ((x 1))
(define (f y) (+ x y))
(set! x 2)
(f 3) )
它的计算结果是5而不是4。考虑到Scheme促进了静态作用域,这是令人惊讶的。在闭包中允许后续的变异影响封闭环境中的绑定似乎恢复到某种动态范围。有什么具体的理由允许这样做吗
编辑:
我意识到上面的代码不太明显,无法揭示我所关心的问题。我将另一段代码放在下面:
(define x 1)
(define (f y) (+ x y))
(set! x 2)
(f 3) ; evaluates to 5 instead of 4
不,这不是动态范围界定。请注意,您的
define
这里是一个内部定义,只能由let
中的代码访问。具体而言,f
未在模块级定义。所以什么也没有泄露出去
内部定义在内部实现为letrec
(R5RS)或letrec*
(R6RS)。因此,它被视为与以下内容相同(如果使用R6RS语义):
这里有两个概念让你感到困惑:范围界定和通过记忆的间接性。词法范围保证对
x
的引用始终指向let
绑定中x
的绑定
在您的示例中没有违反这一点。从概念上讲,let
绑定实际上是在内存中创建一个新位置(包含1
),该位置是绑定到x
的值。当取消引用该位置时,程序将查找该内存位置的当前值。当您使用set代码>,它设置内存中的值。只有能够访问绑定到x
(通过词法范围)的位置的各方才能访问或修改内存中的内容
相反,动态范围允许任何代码更改您在f
中引用的值,而不管您是否授予了对绑定到x
的位置的访问权限。比如说,
(define f
(let ([x 1])
(define (f y) (+ x y))
(set! x 2)
f))
(let ([x 3]) (f 3))
将在一个具有动态范围的假想方案中返回6
。允许这种变异是非常好的。它允许您定义具有内部状态的对象,这些对象只能通过预先安排的方式访问:
(define (adder n)
(let ((x n))
(lambda (y)
(cond ((pair? y) (set! x (car y)))
(else (+ x y))))))
(define f (adder 1))
(f 5) ; 6
(f (list 10))
(f 5) ; 15
除非通过f
函数及其已建立的协议,否则无法更改x
——正是因为Scheme中的词法范围
x
变量是指内部环境框架中属于定义内部lambda
的let
的存储单元,因此返回lambda
及其定义环境的组合,也称为“闭包”
如果你没有提供变异这个内部变量的协议,没有什么可以改变它,因为它是内部的,我们早就离开了定义范围:
(set! x 5) ; WRONG: "x", what "x"? it's inaccessible!
编辑:你的新代码,它完全改变了你问题的含义,也没有问题。这就像我们仍然在定义环境中一样,所以内部变量仍然是可以访问的
更成问题的是以下几点
(define x 1)
(define (f y) (+ x y))
(define x 4)
(f 5) ;?? it's 9.
我希望第二个定义不会干扰第一个定义,但是说define
就像set代码>位于顶层
闭包将它们的定义环境打包在一起顶级环境始终可访问。
f
引用的变量x
位于顶级环境中,因此可以从同一范围内的任何代码访问。也就是说,任何代码。我的答案是显而易见的,但我认为没有其他人触及过它,所以让我说:是的,它很可怕。你在这里真正观察到的是,变异使得你很难对你的程序将要做什么进行推理。纯函数代码——没有变异的代码——在使用相同的输入调用时总是产生相同的结果。具有state和mutation的代码没有此属性。可能用相同的输入调用函数两次会产生不同的结果。你说得对。这不是动态范围界定(我说的是“有点”,也许我应该说的是“似乎”),我关心的是允许这种行为是否好。至少我看不到任何积极的结果。您可以定义一个函数f
,其中使用全局变量x
。稍后您将设置编码>x
并调用f
,它会爆炸。在我看来,创建一个只关闭而不关闭商店的环境是一个糟糕的设计选择。好吧,我想我现在更好地理解你的问题了。关闭门店还有其他问题:如何确定要关闭多少门店?对于环境,从程序的文本中可以明显看出:只有范围中的引用。对于存储,如果f
调用g
,f
是否必须关闭g
访问的内存位置?另外,这将如何影响同时使用您关闭的相同内存位置的程序?我认为f
不必关闭g
可以访问的存储,即使f
调用g
,因为g
已经关闭了它。我还没有考虑涉及并发的可能后果。我同意这个模拟对象特性是合理的。但是请注意,set示例中的code>出现在f
中。自然,它可以变异x
(set!x 5)
此处将失败,但因为x
是加法器的局部变量,即它不在范围内。我已经更新了问题中的代码片段。你可以看到set代码>出现在f
之外,但仍然可以变异在f
的闭包中关闭的变量。我反对的就是这个用例。我认为仍然可以支持您举例说明的功能,并且不允许设置代码>从闭包外部到闭包中关闭的变量。我认为我反对将闭包中关闭的变量进行变异
(define x 1)
(define (f y) (+ x y))
(define x 4)
(f 5) ;?? it's 9.