方案范围(定义和让)

时间:2013-02-18 23:06:21

标签: scheme scoping racket

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

如果我有

(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)的返回值?为什么返回值“未定义”?

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

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

返回00。

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

3 个答案:

答案 0 :(得分:4)

您在第二种情况下遇到的问题是(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
  )

答案 1 :(得分:1)

您的第二个和第三个代码段不是Scheme(R5RS,R6RS和R7RS都不是)。 <body>let和其他)的<body> -> <definition>* <sequence> <sequence> -> <command>* <expression> <command> -> <expression> 定义为:

define

因此<definition>display无法关注<expression>> (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)> > )。您可能会得到令人困惑的结果,因为编译器/解释器错误地处理了'let'的扩展。

以下是'好'的R6RS编译器的作用:

{{1}}

答案 2 :(得分:0)

情况1:f的主体绑定到两个调用中的最外层let,导致00作为静态范围需要。

案例2:我对此并不十分肯定,但内部(define x 4)会影响最外面的x = 0绑定,并且尽管在调用f之后是文本上的,但仍在整个范围内。然后,一些评估技巧的顺序使得第一次调用在新的x绑定完全初始化之前发生,因此它是“未初始化的”。内部的第二次调用在一切都被初始化之后发生,所以4。

案例3:现在我们已经明确地将letrec和define放在不同的范围中,f显然是指最外面的let。定义在这里什么都不做。