阅读有关声明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后,它会打印{ {1}}?
答案 0 :(得分:3)
让我们一步一步走。
(defun fn ()
(print x))
这定义了fn
。
由于引用的x
变量未以词法方式声明,因此我们会收到警告。
免费引用变量通常本地假设为special
,并带有免费声明 1 。这在标准中没有定义,但是大多数实现都是这样做并发出警告。
同样的原则适用于评估文件或REPL中的顶级表单(setq x ...)
。
这些情况都不应该全球将x
声明为special
。
(describe 'x)
x
只是一个符号。它没有发生任何事情全球。
(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
的第一个绑定是本地声明special
。因此,fn
会选择它。
与x
的第二次结合更为相似,只是为x
创建了一个新的动态绑定,在调用fn
后解除了该绑定。
对x
的第三个绑定是词汇绑定,因为除非有全局 special
声明或本地 {,否则每个绑定都是词法。绑定变量的{1}}绑定声明 1 。
因此,获取special
的最新动态绑定的fn
会选择x
。
dinamic_1st_binding
表单中的x
使用print
的任何封闭含义,因此在x
special
被声明为特殊时选择x
和词汇x
{1}}当没有。
(defvar x 'dinamic_global_binding)
此全球将x
声明为special
,并将其symbol-value
设置为dinamic_global_binding
。
符号x
作为变量的每次使用现在都受到此全局 special
声明的污染。从现在开始,没有标准的方法可以将代码绑定或引用名为x
的变量作为词法变量。
(describe 'x)
我们现在观察前一种形式的副作用。 x
全球特殊,其当前动态值为dinamic_global_binding
。
(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
的所有绑定都是特殊的。 ∎
所以,以下示例:
(defun fn2 ()
(print y))
(let ((y 1))
(fn2)
(locally (declare (special y))
(fn2)))
不会动态let
绑定y
。它只会在locally
引用y
的词法下使代码通过 free 声明将其视为动态变量。在这种情况下,对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
的第一个绑定具有绑定 special
声明,因此它是动态绑定。
y
的第二个绑定没有绑定 special
声明,y
不是全局 special
,所以这是一个词汇绑定。
因此,y
的访问权限也取决于y
的(缺失)声明。
第二个声明是 free 声明,使对变量y
的引用被视为动态。因此,此 free 声明不会更改以前对名为y
的变量的绑定。
以下示例:
(let ((y 1))
(print y)
(locally (declare (special y))
(print y)))
将在第二个print
表单中失败,因为它对y
的引用是动态的,并且y
没有建立的动态绑定,即y
未绑定且发出unbound-variable
错误信号。
答案 1 :(得分:2)
如果有一个x的词汇绑定。
在第二种情况下,没有词法绑定。这只是你有符号,但这是误导,因为它清楚地表明最后的x也是动态绑定的。
答案 2 :(得分:1)
Defvar
et al。宣布变量为全球特殊。这意味着即使您省略special
声明,此变量的每个绑定都是动态的。
在第二次运行中,将x
绑定到'lexical-1st-binding
会产生误导,因为无论如何它都是动态绑定。