3

在阅读了关于声明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被defvar'ed之前打印DINAMIC_1ST_BINDING并且在变量x被defvar'ed之后打印LEXICAL_1ST_BINDING

4

3 回答 3

6

让我们一步一步来。

表格 1

(defun fn ()
  (print x))

这定义了fn.

由于引用的x变量没有在词法上声明,我们得到一个警告。

自由引用的变量通常在本地假设special为自由声明1。这在标准中没有定义,但大多数实现都会这样做并发出警告。

同样的原则也适用于评估文件或 REPL 中的顶级表单(setq x ...)

这些情况都不应该全局声明xspecial.

表格 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))

第一个绑定x本地声明。所以,会捡起来。specialfn

第二个绑定到x更多相同,只是创建一个新的动态绑定x,它在调用后展开fn

第三个绑定x是词法绑定,因为每个绑定都是词法绑定,除非绑定变量有全局 special声明或局部 special绑定声明1

因此,fn拾取 的最新动态绑定的x将拾取dinamic_1st_binding

形式中的xsprint使用 的任何封闭含义x,因此special x在它被声明为特殊时选择 ,而在非特殊时选择词法x

表格 4

(defvar x 'dinamic_global_binding)

全局声明xspecial并将其设置symbol-valuedinamic_global_binding

现在,将符号x作为变量的每次使用都受到此全局 special声明的污染。从现在开始,没有标准的方法可以让代码绑定或引用一个名为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都是特殊的。∎<br/>



  1. 如果一个声明以执行绑定的形式为建立绑定提供了意义,则该声明是受约束的,否则是自由,即如果它仅对绑定的引用/使用赋予意义。

所以,下面的例子:

(defun fn2 ()
  (print y))

(let ((y 1))
  (fn2)
  (locally (declare (special y))
    (fn2)))

不会动态let绑定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有一个绑定 special声明,所以它是一个动态绑定。

的第二个绑定y没有绑定 special声明并且y不是global special,所以它是一个词法绑定。

因此,对 的访问y也受(缺少)关于y.

第二个声明是自由声明,使对变量的引用y被视为动态的。因此,这个自由声明y不会改变之前对命名变量的绑定。

下面的例子:

(let ((y 1))
  (print y)
  (locally (declare (special y))
    (print y)))

在第二种print形式中将失败,因为它的引用y是动态的,并且没有为 建立动态绑定y,即y未绑定并发出unbound-variable错误信号。

于 2013-07-31T16:02:03.153 回答
2

如果存在 x 的词法绑定。

情况二没有词法绑定。只是你有符号,但这是一种误导,因为它清楚地表明最后一个 x 也是动态绑定的。

于 2013-07-28T03:24:12.690 回答
2

Defvar 等。宣布变量是全局特殊的。这意味着这个变量的每个绑定都是动态的,即使你省略了special声明。

在您的第二次运行中,绑定x'lexical-1st-binding具有误导性,因为它无论如何都是动态绑定。

于 2013-07-29T13:25:43.547 回答