方案范围(定义和让)

use*_*086 5 scheme scoping racket

所以我知道在Scheme中定义的是动态作用域,并且允许静态作用域,但是下面的内容让我困惑:

如果我有

(let ((x 0))
  (define f (lambda () x))
  (display (f))
  (let ((x 1))
    (display (f))
    )
  )
Run Code Online (Sandbox Code Playgroud)

它将显示00.到目前为止一切顺利.但是,如果我为x添加额外的定义,如下所示:

(let ((x 0))
  (define f (lambda () x))
  (display (f))
  (define x 4)
  (let ((x 1))
    (display (f))
    )
  )
Run Code Online (Sandbox Code Playgroud)

它将显示undefined4.为什么是这样?为什么评估f 之后定义x 会影响(f)的返回值?为什么返回值"未定义"?

还值得一提的是,用letrec而不是define绑定f也会起作用:

(let ((x 0))
  (letrec ((f (lambda () x)))
  (display (f))
  (define x 4)
  (let ((x 1))
    (display (f))
    )
  )
)
Run Code Online (Sandbox Code Playgroud)

返回00.

注意:我使用了DrRacket,上面写着"Pretty Big"

Gre*_*ott 5

您在第二种情况下遇到的问题是为定义它的整个范围(define x 42)创建x了一个变量。现在,虽然变量是为整个范围定义的,但它的在实际行之前是未定义的。(define x 42)

(let ()
  ;; up here, `x` is defined but has an undefined value
  ;; ...
  (define x 42)
  ;; down here `x` has the value 42
  ;; ...
  )
Run Code Online (Sandbox Code Playgroud)

它的表现更像这样:

(let ([x 'undefined])
  ;; ... up here, `x` is 'undefined
  (set! x 42)
  ;; ... down here, `x` is 42
  )
Run Code Online (Sandbox Code Playgroud)


hza*_*zap 0

情况 1:f 的主体在两次调用中都绑定到最外层的 let,导致静态作用域所需的 00。

情况 2:我对此不太确定,但内部(define x 4)阴影最外面的 x=0 绑定,并且在整个范围内,即使它在文本上是在调用 f 之后。然后,某种评估顺序使得第一次调用发生在新的 x 绑定完全初始化之前,因此它是“未初始化的”。内部 let 中的第二次调用发生在所有内容都初始化之后,所以 4。

情况3:现在我们已经明确地将letrec 和define 放在不同的作用域中,f 显然指的是最外面的let。该定义在这里不执行任何操作。