阅读有关声明的文档后 SPECIAL , 特殊运算符 LET , 宏 DEFVAR ,以及 StackOverflow 上关于 Common Lisp 中动态与词法作用域的几个问题,例如,this ,在 SBCL 中评估这些表单后,我仍然无法理解以下行为。
;; x is a free variable
CL-USER> (defun fn ()
(print x))
; in: DEFUN FN
; (PRINT X)
;
; caught WARNING:
; undefined variable: X
;
; compilation unit finished
; Undefined variable:
; X
; caught 1 WARNING condition
FN
CL-USER> (describe 'x)
COMMON-LISP-USER::X
[symbol]
; No value
CL-USER> (let ((x 'dinamic_1st_binding))
(declare (special x))
(print x)
(fn)
(let ((x 'dinamic_2nd_binding))
(declare (special x))
(print x)
(fn))
(let ((x 'lexical_1st_binding))
(print x)
(fn))
(values))
DINAMIC_1ST_BINDING
DINAMIC_1ST_BINDING
DINAMIC_2ND_BINDING
DINAMIC_2ND_BINDING
LEXICAL_1ST_BINDING
DINAMIC_1ST_BINDING
; No value
;; x is defvar'ed as a top level form
CL-USER> (defvar x 'dinamic_global_binding)
X
CL-USER> (describe 'x)
COMMON-LISP-USER::X
[symbol]
X names a special variable:
Value: DINAMIC_GLOBAL_BINDING
; No value
CL-USER> (let ((x 'dinamic_1st_binding))
(declare (special x))
(print x)
(fn)
(let ((x 'dinamic_2nd_binding))
(declare (special x))
(print x)
(fn))
(let ((x 'lexical_1st_binding))
(print x)
(fn))
(values))
DINAMIC_1ST_BINDING
DINAMIC_1ST_BINDING
DINAMIC_2ND_BINDING
DINAMIC_2ND_BINDING
LEXICAL_1ST_BINDING
LEXICAL_1ST_BINDING
; No value
为什么第三次调用
fn
, 在变量 x
之前已定义变量,打印 DINAMIC_1ST_BINDING
在变量 x
之后是defvar'ed它打印LEXICAL_1ST_BINDING
?
最佳答案
让我们一步一步来。
表格 1
(defun fn ()
(print x))
这定义了
fn
.由于引用了
x
变量没有在词法上声明,我们得到一个警告。自由引用的变量通常是 本地 假设
special
免费声明1。这在标准中没有定义,但大多数实现都会这样做并发出警告。同样的原则也适用于评估顶级表单
(setq x ...)
在文件或 REPL 中。这些情况都不应该全局 声明
x
如special
.表格 2
(describe 'x)
x
只是一个象征。什么都没发生全局 .表格 3
(let ((x 'dinamic_1st_binding))
(declare (special x))
(print x)
(fn)
(let ((x 'dinamic_2nd_binding))
(declare (special x))
(print x)
(fn))
(let ((x 'lexical_1st_binding))
(print x)
(fn))
(values))
第一次绑定(bind)到
x
是 本地 声明 special
.所以,fn
会捡起来的。第二次访问
x
差不多,只是为 x
创建一个新的动态绑定(bind)。 ,在调用 fn
后解除.第三次绑定(bind)到
x
是一个词法绑定(bind),因为每个绑定(bind)都是词法的,除非有 全局 special
声明或 本地 special
绑定(bind)变量的绑定(bind)声明1。因此,
fn
,它将获取 x
的最新动态绑定(bind), 会接dinamic_1st_binding
.x
s 在 print
表格使用 x
的任何封闭含义,因此选择 special
x
当它被声明为特殊且词法 x
没有的时候。表格 4
(defvar x 'dinamic_global_binding)
这个全局 声明
x
如special
并设置其symbol-value
至dinamic_global_binding
.符号
x
的每次使用作为一个变量现在被这个 污染了全局 special
宣言。从现在开始,没有标准的方法可以让代码绑定(bind)或引用名为 x
的变量。作为词法变量。表格 5
(describe 'x)
我们现在观察前一种形式的副作用。
x
是 全局 特殊,当前动态值为dinamic_global_binding
.表格 6
(let ((x 'dinamic_1st_binding))
(declare (special x))
(print x)
(fn)
(let ((x 'dinamic_2nd_binding))
(declare (special x))
(print x)
(fn))
(let ((x 'lexical_1st_binding))
(print x)
(fn))
(values))
x
的所有绑定(bind)很特别。 ∎所以,下面的例子:
(defun fn2 ()
(print y))
(let ((y 1))
(fn2)
(locally (declare (special y))
(fn2)))
不做
let
绑定(bind) y
动态的。它只是使代码在 locally
下的词法上指y
通过自由声明将其视为动态变量。在这种情况下,两次调用 fn2
将发出错误信号。以下:
(let ((y 1))
(declare (special y))
(print y)
(let ((y 2))
(print y)
(locally (declare (special y))
(print y))
(print y)))
将打印:
1
2
1
2
y
的第一个绑定(bind)有界special
声明,所以它是一个动态绑定(bind)。y
的第二次绑定(bind)没有界限special
声明和 y
不是 全局 special
,所以它是一个词法绑定(bind)。因此,访问
y
还受到关于 y
的(缺失)声明的限制。 .第二个声明是自由声明,引用变量
y
被视为动态的。所以,之前的绑定(bind)到一个名为 y
的变量。这个自由声明不会改变。下面的例子:
(let ((y 1))
(print y)
(locally (declare (special y))
(print y)))
将在第二个
print
中失败表格,因为它引用了 y
是动态的,并且没有为 y
建立动态绑定(bind),即 y
未绑定(bind)且 unbound-variable
发出错误信号。
关于lisp - 关于 Common Lisp 作用域的微妙之处(动态与词法),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17904350/