lisp - 关于 Common Lisp 作用域的微妙之处(动态与词法)

标签 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'ed它打印LEXICAL_1ST_BINDING ?

最佳答案

让我们一步一步来。

表格 1

(defun fn ()
  (print x))

这定义了 fn .

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

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

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

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

第一次绑定(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)

这个全局 声明 xspecial并设置其symbol-valuedinamic_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)很特别。 ∎


  • 如果声明以执行绑定(bind)的形式为建立绑定(bind)赋予意义,则声明是绑定(bind)的,否则是自由的,即如果它仅对绑定(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/

    相关文章:

    lisp - 来自 Steel Bank Common Lisp & Slime 的最大调试信息

    lisp - 是否有通用的 lisp 包命名约定?

    c++ - 继承时的变量范围

    lisp - 为什么这会炸毁 Lispworks 中的堆?

    variables - LISP:将变量/符号传递给函数而不评估

    mysql - cl-dbi 从 sbcl 查询 mysql 时出现错误 - 非法 :UTF-8 character starting at position 18

    lisp - 函数 (OccurencesOfPrimes < list >) 计算(可能嵌套的)列表中素数的数量

    javascript - 如何在 onreadystatechange 函数之外访问 XMLHttpRequest 值

    python - if/else 内的全局范围用于运行中位数

    列表操作