关于Common Lisp范围的微妙之处(动态与词汇)

时间:2013-07-28 02:13:38

标签: lisp common-lisp scoping lexical-scope dynamic-scope

阅读有关声明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}}?

3 个答案:

答案 0 :(得分:3)

让我们一步一步走。

表格1

(defun fn ()
  (print x))

这定义了fn

由于引用的x变量未以词法方式声明,因此我们会收到警告。

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

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

这些情况都不应该全球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))

x的第一个绑定是本地声明special。因此,fn会选择它。

x的第二次结合更为相似,只是为x创建了一个新的动态绑定,在调用fn后解除了该绑定。

x的第三个绑定是词汇绑定,因为除非有全局 special声明或本地 {,否则每个绑定都是词法。绑定变量的{1}}绑定声明 1

因此,获取special的最新动态绑定的fn会选择x

dinamic_1st_binding表单中的x使用print的任何封闭含义,因此在x special被声明为特殊时选择x和词汇x {1}}当没有。

表格4

(defvar x 'dinamic_global_binding)

全球x声明为special,并将其symbol-value设置为dinamic_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的所有绑定都是特殊的。 ∎




  1. 声明是绑定如果它赋予以执行绑定的形式建立绑定的意义,并且 free ,否则,即它只提供含义引用/使用约束力。
  2. 所以,以下示例:

    (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会产生误导,因为无论如何它都是动态绑定。