方案中的词法范围

lexical scoping in scheme

我正在尝试理解词法和动态范围的概念以及它们之间的区别。 我们来看看下面的代码:

(let ((a 1)
      (b 2))
  (letrec ((f (lambda () F))
            (g (lambda (c) G)))
    (lambda (d)
      (+ (f) (g b)))))

对于表达式 F 和 G,哪些变量在 (lambda(d)...) 的词法范围内?

(lambda(d)...)d 作为绑定变量,将 fg a b 和所有全局范围作为自由变量.

编辑

只是为了演示 Scheme 和一些其他语言中的代码,其中相同的绑定是动态的。由于您的两个函数都不会相互调用,因此您不妨将它们保持在相同的位置 let:

(define test
  (let ((a 1)
        (b 2)
        (f (lambda () F))
        (g (lambda (c) G)))
    (lambda (d)
      (+ (f) (g b)))))

;; for the test we need the F and G defined
(define F 10)
(define G 20)

(test 4) ; ==> 30 (aka (+ F G))

发生的事情是,当 lambda 被求值时,即使 let 消失了,它从词法范围使用的变量仍然存在。在动态范围内,这是不正确的:

(test 4)
; ERROR f: unbound identifier

原因是 abfg 在评估兰巴时存在 none 变量被捕获就像在词法范围内一样,因此当过程 test 被创建时,局部变量的 none 就不再存在了。其实你还不如这样写:

;; in a dynamic lisp this s the same 
(define test
  (lambda (d)
    (+ (f) (g b))))

并且在调用函数(又名动态)时必须确保变量存在

(define g (lambda (v) 1337))
(define f (lambda () 3.1415927))
(define b 13)
(test 4) ; ==> 1340.1415927

现在,如果您要在全局范围内添加上述定义并保留原始定义,您仍然会得到 30,因为词法 lisp 使用更紧密的词法绑定而不是全局绑定。

另一个很好的例子是:

(define x 10)
(define (test v)
  (+ x v))

(let ((x 20))
  (test 10))
; ==> ?

在词法 lisp 中,结果总是 20,因为 test 不知道 let,因为它不在其词法范围内。它的 x 始终是全局绑定 x。在动态 lisp 中,let 中的 x 从运行时的角度来看是最接近的,这将导致 30,因为 test 中的 xlet 中的 x 相同,它隐藏全局 x.