我正在阅读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
的环境中调用该函数。您可以通过在绑定x
的let
中调用它来实现这一点。
然而,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
的定义,而不存在此类变量。