收到“未定义变量”警告,即使已定义

时间:2019-04-19 07:01:25

标签: common-lisp sbcl

在sbcl repl中执行以下代码时,我收到变量测试用例n的“未定义变量”警告。我在论坛上经历了类似的问题,如果在未先定义的情况下设置了setf / setq'ed变量,就会出现警告。但是我已经使用defparameter定义了这些变量,但仍然得到警告。

我注意到的一件事是,如果我未在format语句中引用变量,则不会发生警告。 我也尝试过使用defvar。但它仍在发出警告。 有人可以帮助我理解为什么即使在定义变量时在语句中使用时也会引发警告吗?

    (defun main ()
           (defvar test-cases 10)
           (defvar l 12)
           (defvar n 13)
           (format t "~a ~a ~a" test-cases l n))
; in: DEFUN MAIN
;     (FORMAT T "~a ~a ~a" TEST-CASES L N)
; 
; caught WARNING:
;   undefined variable: N
; 
; caught WARNING:
;   undefined variable: TEST-CASES
; 
; compilation unit finished
;   Undefined variables:
;     N TEST-CASES
;   caught 2 WARNING conditions
WARNING: redefining COMMON-LISP-USER::MAIN in DEFUN

1 个答案:

答案 0 :(得分:4)

以下内容解释了为什么,您会出错,但是请注意,很少需要从内部函数中定义全局变量,大多数情况下,您将使用LET进行局部绑定

这是一个简化的测试用例:

(defun foo () (defvar bar nil) bar)

在编译foo时,bar未定义(假设有新的CL环境)。这就是编译器抱怨的原因。但是,如果您调用foo,则将在全局范围内声明变量,然后返回其值。

DEFVAR的调用仅在执行时或在其为顶级形式的编译时才对全局环境有效:

  

但是,下面描述的编译时副作用仅在它们以顶级形式出现时才会发生。

     

...

     

副作用::如果defvardefparameter表单显示为顶级表单,则编译器必须认识到该名称被宣布为特殊名称。但是,它既不能求值初值形式,也不能在编译时分配名为name的动态变量。

因此,在函数体内编译对defvar的调用不会将符号声明为特殊变量。