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

What's happening when the special variable is declared during compile time

当我想测试 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,问题消失,打印结果为12 当我调用 (test1 test-out).
  3. 我将 (defvar test-out 2) 更改为 (defvar test-out-1 2)(更改动态变量名称)。然后重新编译整个文件(这次没有叫test-out的动态变量,test1参数的名字是test-out),问题就消失了。
  4. 3后,我调用(defvar test-out 2)(test1 test-out)。这次,它打印出正确答案:12.
  5. 4后,我重新编译test1,然后运行(test1 test-out),打印出11又一次,问题又出现了。

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

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

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
    ...))