【问题标题】:What's happening when the special variable is declared during compile time在编译期间声明特殊变量时会发生什么
【发布时间】:2019-04-21 21:13:35
【问题描述】:

当我想测试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*,但我认为它只是为了方便程序员告诉动态变量。

我对@9​​87654325@ 函数有点困惑,看起来locally declare 没有将test-out 指向外部的动态变量。

谁能向我解释test1 函数的行为?谢谢

更新:

  1. 我给了一个新的动态变量(defvar test-out-1 3),像(test1 test-out-1)一样调用它,仍然得到打印结果11
  2. 我将test1的参数名称从test-out更改为test-out1,重新编译test1,问题消失,当我调用(test1 test-out)时,打印输出结果为12。李>
  3. 我将(defvar test-out 2) 更改为(defvar test-out-1 2)(更改动态变量名称)。然后重新编译整个文件(这次没有动态变量叫test-outtest1参数的名字是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 时,问题会自行解决。

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

【问题讨论】:

    标签: lisp common-lisp dynamic-binding


    【解决方案1】:

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

    【讨论】:

    • 那么,对于我的 Update 4(defvar test-out 2)test1 编译后不会影响 test1,因为它已经编译了?
    • @ccQpein:对,DEFVAR 通常不会对早期编译的代码产生影响。如果较早编译的代码被编译为使用词法绑定,则以后在编译/加载/执行 DEFVAR 时不会更改。
    • @Rainer Joswig,这不是也可以接受吗:(defvar *x* 10) (defun foo () (let ((*x* ...)) ...))
    猜你喜欢
    • 2011-07-06
    • 1970-01-01
    • 1970-01-01
    • 2018-05-01
    • 1970-01-01
    • 2018-08-04
    • 1970-01-01
    • 1970-01-01
    • 2016-12-28
    相关资源
    最近更新 更多