在编译期间声明特殊变量时会发生什么

时间:2019-04-21 21:13:35

标签: lisp common-lisp dynamic-binding

当我想测试locallydeclare时,我在通用Lisp代码中遇到了一种不寻常的情况:

(defvar test-out 2) ;; make a dynamic variable

;; function below just simply re-write from locally doc
(defun test (out)
  (declare (special out))
  (let ((out 1))
    (print out) ;; => 1
    (print (locally (declare (special out)) out)))) ;; => 2

;; when argument has same name as outside dynamic variable
(defun test1 (test-out)
  (declare (special test-out))
  (let ((test-out 1))
    (print test-out) ;; => 1
    (print (locally (declare (special test-out)) test-out)))) ;; => also 1

我知道动态变量的正确名称应该为*test-out*,但我认为这只是为了程序员方便地告诉动态变量。

我对test1函数有些困惑,看来locally declare并没有将test-out指向外部的动态变量。

有人可以向我解释test1函数的行为吗?谢谢

更新

  1. 我给了一个新的动态变量(defvar test-out-1 3),并像(test1 test-out-1)那样调用它,仍然得到打印结果11
  2. 我将test1的参数名称从test-out更改为test-out1,重新编译test1,问题消失了,打印出的结果是1和{{ 1}},当我致电2时。
  3. 我将(test1 test-out)更改为(defvar test-out 2)(更改动态变量名称)。然后重新编译整个文件(这次没有动态变量称为(defvar test-out-1 2),并且test-out参数的名称为test1),问题消失了。
  4. 3 之后,我分别呼叫test-out(defvar test-out 2)。这次,它打印出正确的答案:(test1 test-out)1
  5. 4 之后,我再次重新编译2,然后运行test1,它打印出(test1 test-out)1,再次出现问题。

如果我猜对了,则1出于某种原因在编译时,其参数名称会连接到动态变量test1。这就是为什么当我什至使用不同的值调用时都会收到错误结果的原因,但是,当我在重新编译测试之前使用不同的参数名称或干净的动态变量test-out重新编译test1时,问题会自行解决。

如果是这样,我仍然不明白为什么编译功能会受到环境中动态变量的影响。

1 个答案:

答案 0 :(得分:6)

DEFVAR声明一个变量为 special -这意味着它们在绑定时将使用动态绑定,对此类变量的访问将查找动态绑定。在全球和所有绑定级别。现在和将来。

此后, ALL 的使用和新代码中该变量的绑定将自动声明为特殊。甚至是本地LET绑定。在所有级别上。无法将其声明为 unspecial 。因此,现在不需要test1函数中的局部特殊声明,因为它已经被声明为特殊。现在,即使没有显式声明,它的每次使用或绑定都在使用动态绑定。

这也是为什么将任何DEFVARDEFPARAMETER变量都写为*variablename*的原因,以防止意外地将所有具有相同名称的变量声明为特殊变量。

避免:

(defvar x 10)         ; here X is explicitly declared special

(defun foo (x)        ; here X is implicitly declared special
  (let ((x ...))      ; here X is implicitly declared special
    ...))   

要做:

(defvar *x* 10)       ; here *X* is declared special

(defun foo (x)        ; here X is lexical
  (let ((x ...))      ; here X is lexical
    ...))