在编译期间声明特殊变量时发生了什么
What's happening when the special variable is declared during compile time
当我想测试 locally
和 declare
时,我在我的普通 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
函数的行为吗?谢谢
更新:
- 我给了一个新的动态变量
(defvar test-out-1 3)
,并像(test1 test-out-1)
一样调用它,仍然得到打印结果1
和1
。
- 我把
test1
的参数名从test-out
改为test-out1
,重新编译test1
,问题消失,打印结果为1
和2
当我调用 (test1 test-out)
.
- 我将
(defvar test-out 2)
更改为 (defvar test-out-1 2)
(更改动态变量名称)。然后重新编译整个文件(这次没有叫test-out
的动态变量,test1
参数的名字是test-out
),问题就消失了。
- 3后,我调用
(defvar test-out 2)
,(test1 test-out)
。这次,它打印出正确答案:1
和 2
.
- 4后,我重新编译
test1
,然后运行(test1 test-out)
,打印出1
和1
又一次,问题又出现了。
如果我猜对了,当 test1
编译时,出于某种原因,它的参数名称连接到动态变量 test-out
。这就是为什么我什至用不同的值调用时都会收到错误的结果,但是,当我在重新编译测试之前使用不同的参数名称或干净的动态变量 test-out
重新编译 test1
时,问题会自行解决。
如果是这样,我仍然不明白为什么编译函数会受到环境中动态变量的影响。
DEFVAR
将变量声明为 special - 这意味着它们将在绑定时使用动态绑定,并且访问此类变量将查找动态绑定。在全球范围内和所有具有约束力的层面上。现在和将来。
从那时起,ALL 在新代码中使用和绑定该变量将自动声明为特殊变量。甚至本地 LET 绑定。在各个层面。没有办法声明它 unspecial。因此,现在不需要 test1
函数中的局部特殊声明,它已经被声明为特殊的。它的每次使用或绑定,即使没有显式声明,现在都在使用动态绑定。
这也是为什么任何 DEFVAR
或 DEFPARAMETER
变量都应该写成 *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
...))
当我想测试 locally
和 declare
时,我在我的普通 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
函数的行为吗?谢谢
更新:
- 我给了一个新的动态变量
(defvar test-out-1 3)
,并像(test1 test-out-1)
一样调用它,仍然得到打印结果1
和1
。 - 我把
test1
的参数名从test-out
改为test-out1
,重新编译test1
,问题消失,打印结果为1
和2
当我调用(test1 test-out)
. - 我将
(defvar test-out 2)
更改为(defvar test-out-1 2)
(更改动态变量名称)。然后重新编译整个文件(这次没有叫test-out
的动态变量,test1
参数的名字是test-out
),问题就消失了。 - 3后,我调用
(defvar test-out 2)
,(test1 test-out)
。这次,它打印出正确答案:1
和2
. - 4后,我重新编译
test1
,然后运行(test1 test-out)
,打印出1
和1
又一次,问题又出现了。
如果我猜对了,当 test1
编译时,出于某种原因,它的参数名称连接到动态变量 test-out
。这就是为什么我什至用不同的值调用时都会收到错误的结果,但是,当我在重新编译测试之前使用不同的参数名称或干净的动态变量 test-out
重新编译 test1
时,问题会自行解决。
如果是这样,我仍然不明白为什么编译函数会受到环境中动态变量的影响。
DEFVAR
将变量声明为 special - 这意味着它们将在绑定时使用动态绑定,并且访问此类变量将查找动态绑定。在全球范围内和所有具有约束力的层面上。现在和将来。
从那时起,ALL 在新代码中使用和绑定该变量将自动声明为特殊变量。甚至本地 LET 绑定。在各个层面。没有办法声明它 unspecial。因此,现在不需要 test1
函数中的局部特殊声明,它已经被声明为特殊的。它的每次使用或绑定,即使没有显式声明,现在都在使用动态绑定。
这也是为什么任何 DEFVAR
或 DEFPARAMETER
变量都应该写成 *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
...))