When是lambda绑定中的标识符



我正在阅读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: :x 中模块中的未绑定标识符

这是因为词法范围。

(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)

正如您在动态作用域中可能看到的那样,很难判断过程是否正确,因为每次调用时调用的环境都会发生变化。对于编译器来说,词法范围界定也更容易优化。

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

如果有自由变量,如示例中的x,则需要在绑定了某些x的环境中调用该函数。您可以通过在绑定xlet中调用它来实现这一点。

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

一些阅读可以启发这一点:

[1] 静态与词汇范围界定:静态(词汇)范围界定与动态范围界定(伪码)

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

Scheme具有词法范围。这意味着,当对lambda的主体进行求值时,将使用创建lambda的环境进行求值,而不是从调用lambda的地方进行求值。

因此,当您调用f时,虽然存在x变量,但这并不重要,因为Scheme在您定义f的环境中查找x的定义,而不存在此类变量。

最新更新