声明没有按顺序执行? (let语句中的defvar)

时间:2011-07-03 19:56:41

标签: common-lisp let

我试图将它减少到最小的例子。代码运行时没有错误,产生预期的输出。但它给了我一个警告,我的第一个变量是未定义的。似乎第二个预言陈述并没有“看到”第一个陈述的结果。谢谢你的帮助!

(我最初根本没有在代码中使用progn构造,但是在收到此错误之后我添加了它以查看是否会强制按顺序执行 - 但错误是相同的。)

以下是代码:

(let ((input (open "input.lisp")))
  (progn (defvar var1 (read input))
         (defvar arr1 (make-array var1 :initial-contents (read input))))
  (close input))

(print var1)
(print arr1)

这些是文件“input.lisp”的内容:

9
(10 8 6 4 2 4 6 8 10)

这是我在执行后从sbcl获得的输出(加载“test.lisp”):

; in: DEFVAR ARR1
;     (MAKE-ARRAY VAR1 :INITIAL-CONTENTS (READ INPUT))
; 
; caught WARNING:
;   undefined variable: VAR1
; 
; compilation unit finished
;   Undefined variable:
;     VAR1
;   caught 1 WARNING condition

9 
#(10 8 6 4 2 4 6 8 10) 
T

所以,在我看来,两个定义语句都在执行,但第二个定义语句没有“看到”第一个的结果。它仍然正确构造数组,因为它填充了给定的初始内容。但为什么var1还没有定义?

1 个答案:

答案 0 :(得分:4)

请参阅Hyperspec中的documentation for defvar

  

如果defvardefparameter表单显示为顶级表单,则编译器必须识别该名称已被声明为special

这暗示(并且似乎是SBCL的情况)如果defvar显示为非顶级形式,则编译器无需识别该名称已被声明。那你的defvar怎么没有被编译为顶级表格呢?请参阅section 3.2.3.1, Processing of Top Level Forms(第6点)了解答案:代码周围的let会将其编译为非顶级表单。

因此,您需要在顶层defvar变量,然后在setflet分配它们。


喜欢这个。使用with-open-file而不是openclose通常也更简单。

(defvar var1)
(defvar arr1)

(with-open-file (input "input.lisp" :direction :input)
  (setf var1 (read input))
  (setf arr1 (make-array var1 :initial-contents (read input))))

(print var1)
(print arr1)

您遇到此问题的原因是您将代码放在文件的顶层。这是一个不太常见的事情:正常的Lisp编码风格是将大部分代码放在函数定义中,然后在需要运行它们时调用这些函数。

例如,这是编写此类代码的更典型方式,初始化代码在其自己的函数中。

(defvar *var1* nil "Documentation for var1.")
(defvar *arr1* nil "Documentation for arr1.")

(defun init-from-file (file)
  "Read *var1* and *arr1* from file."
  (with-open-file (input file :direction :input)
    (setf *var1* (read input))
    (setf *arr1* (make-array *var1* :initial-contents (read input)))))

(when (null *var1*) (init-from-file "input.lisp"))