lambda 中的标识符何时绑定?

When is a identifier in a lambda bound?

我正在读SICP。有些事情让我感到困惑。这些都是我自己写的,测试一下

;This procedure bind a symbol `f` to a lambda, and that lambda is not evaluated.
(define f
  (lambda () x))

;create a frame which binds x to 10, and then evaluate f, should return 10.
(let ((x 10))
    (f))

但是DrRacket不接受

x: unbound identifier in module in: x

方案有词法范围。这意味着当计算 lambda 的主体时,它是使用创建 lambda 的环境而不是调用它的环境来计算的。

因此,虽然在调用 f 时存在 x 变量,但这并不重要,因为 Scheme 在您定义 [=11] 的环境中查找 x 的定义=] 其中不存在这样的变量。

您在这里看到的问题是 x 是一个自由变量。 Racket(和大多数语言)中的表达式不能有自由变量(称为 "closed term")[2].

如果您有自由变量,如示例中的 x,则需要在绑定了 x 的某些事件的环境中调用该函数。您可以通过在绑定 x.

let 中调用它来实现这一点

但是,Scheme 是词法范围的[1],这意味着它从定义它的环境中捕获变量。但是您没有在定义 lambda 的范围内定义 x 。因此错误。

一些阅读以启发这一点:

[1] 静态与词法范围:Static (Lexical) Scoping vs Dynamic Scoping (Pseudocode)

[2] 自由变量:https://en.wikipedia.org/wiki/Free_variables_and_bound_variables

这是因为词法范围。

(define f
  (lambda () x))

由于 x 不在词法范围内,因此 必须是全局变量 。因此,您可以执行以下操作:

(define x 10)
(f) ;=> 10

要使其成为词法变量,它必须在创建时已经存在:

(define f
  (let ((x 10))
    (lambda () x)))

在这个例子中,由于 x 是一个绑定词法变量,因此 lambda 中的 x 是相同的,因为 lambda 继承了它被评估的环境.

是的! lambda 表达式 被评估 以便它成为过程对象并且 define 将全局符号 f 分配给该对象。

证明尝试应用列表结构:

('(lambda (x) x) 10) ; gets error 'application: not a procedure'

在具有动态作用域的 LISP 中,过程没有环境,环境是在调用时从运行时获取的:

(define (test)
  some-value)

(define (call-test)
  (let ((some-value 10))
    (test))) ; => 10

(test) ; => error (unbound variable)

正如您在动态作用域中看到的那样,很难判断过程是否正确,因为每次调用时它所处的环境都会发生变化。编译器优化词法范围也容易得多。