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

Common lisp 语句没有按顺序执行?(let语句中的defvar),common-lisp,let,Common Lisp,Let,我试着把它简化成最小的例子。代码运行时没有错误,产生了预期的输出。但它给了我一个警告,我的第一个变量是未定义的。progn的第二条语句似乎没有“看到”第一条语句的结果。谢谢你的帮助 (我最初在代码中根本没有progn构造,但在得到这个错误后,我添加了它,以查看这是否会强制按顺序执行——但错误是相同的。) 代码如下: (let ((input (open "input.lisp"))) (progn (defvar var1 (read input)) (defvar arr

我试着把它简化成最小的例子。代码运行时没有错误,产生了预期的输出。但它给了我一个警告,我的第一个变量是未定义的。progn的第二条语句似乎没有“看到”第一条语句的结果。谢谢你的帮助

(我最初在代码中根本没有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”的内容:

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

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

请参见Hyperspec中的:

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

这意味着(SBCL的情况似乎也是如此)如果
defvar
显示为非顶级表单,那么编译器就不需要识别已声明的名称。那么为什么您的
defvar
s没有被编译为顶级表单呢?请参阅(第6点)以了解答案:围绕代码的
let
会导致将其编译为非顶级表单

因此,您需要在顶层
defvar
变量,然后在
let
中使用
setf
分配它们


像这样。它通常比
open
close
更易于使用

(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"))

好的,谢谢,这很有道理。我没有真正“获取”顶级表单,但我想我理解了……这是否意味着我必须编写“(defvar输入(打开“input.lisp”)),因为我不能使用“let”构造?请参阅代码。如果愿意,您可以使用
let
,但在这种情况下,打开文件的
更好。谢谢。我想使用一个宏来根据输入定义和定义每个变量,这就是为什么defvars首先出现在let语句中的原因。有什么建议吗?我应该先定义变量,然后让宏对每个变量进行设置吗?这是一种方法,或者(具体取决于您正在做什么)让宏生成整个过程:
defvar
setf
等等。您应该始终用耳罩围绕词汇变量。你应该叫他们var1和arr1。否则你会遇到麻烦。我不想看你的密码我不知道你说的耳罩是什么意思?我只调用了变量,因为我想让这个最小的工作示例变得清晰。[…删掉了废话废话…]。编辑:我查了一下耳罩。谢谢你的建议。我遇到的问题是,似乎a)实际上我所有的变量都需要是全局的(“词法的”),或者b)我只想用巨大的嵌套let语句来包装一切。有一个好的中间地带吗?耳罩是围绕变量名的星星。毫无疑问,你以前是偶然发现它们的。你不必相信我的话,而是要读一个真正的专业人士的意见(尼科德摩斯):。哦,是的,我看到耳罩很好,但是如果这些变量如此特殊,我会觉得我有太多了。。。。再次感谢你的建议。whoplisp有点困惑。传统上,特殊变量(由
defvar
声明)获得耳罩,而不是词汇变量(由
let
声明)。这只是一个约定,所以如果不喜欢它,您不必遵循它,但是如果您需要阅读其他Lisp程序员编写的代码,那么了解它是很有价值的。
(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)
(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"))