5

所以我知道在 Scheme 中,define 用于动态范围,而 let 用于静态范围,但以下事情让我感到困惑:

如果我有

(let ((x 0))
  (define f (lambda () x))
  (display (f))
  (let ((x 1))
    (display (f))
    )
  )

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

(let ((x 0))
  (define f (lambda () x))
  (display (f))
  (define x 4)
  (let ((x 1))
    (display (f))
    )
  )

它将显示 undefined4。为什么是这样?为什么在计算 f之后定义 x会影响 (f) 的返回值?为什么返回值“未定义”?

还值得一提的是,将 f 与 letrec 而不是 define 绑定也可以:

(let ((x 0))
  (letrec ((f (lambda () x)))
  (display (f))
  (define x 4)
  (let ((x 1))
    (display (f))
    )
  )
)

返回 00。

注意:我使用 DrRacket 的语言设置为“Pretty Big”

4

3 回答 3

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
  ;; ...
  )

它的行为更像这样:

(let ([x 'undefined])
  ;; ... up here, `x` is 'undefined
  (set! x 42)
  ;; ... down here, `x` is 42
  )
于 2013-02-19T02:00:14.660 回答
1

您的第二个和第三个代码片段不是方案(R5RS、R6RS 和 R7RS 都不是)。( <body>of a letand others) 定义为:

<body> -> <definition>* <sequence>
<sequence> -> <command>* <expression>
<command> -> <expression>

因此 a define(即 a <definition>)不能跟随display(an <expression>)。您可能会得到令人困惑的结果,因为编译器/解释器错误地处理了“let”的扩展。

这是一个“好的”R6RS 编译器所做的:

> (let ((x 0))
  (letrec ((f (lambda () x)))
  (display (f))
  (define x 4)
  (let ((x 1))
    (display (f))
    )
  )
)
Unhandled exception
 Condition components:
   1. &who: define
   2. &message: "a definition was found where an expression was expected"
   3. &syntax:
       form: (define x 4)
       subform: #f
   4. &trace: #<syntax (define x 4)>
> 
于 2013-02-25T04:18:28.363 回答
0

Case 1: the body of f binds to the outermost let in both invocations, resulting in 00 as static scope requires.

Case 2: I'm not very sure about this, but the internal (define x 4) shadows the outermost x=0 binding, and is in scope throughout even though it's textually after the call to f. Then some order of evaluation trickiness makes the first call happen before the new x binding is fully initialized, so it's "uninitialized". The second call in the inner let happens after everything is initialized, so 4.

Case 3: Now that we have explicitly put the letrec and the define in separate scopes, f obviously refers to the outermost let. The define does nothing here.

于 2013-02-19T01:41:33.767 回答