【问题标题】:Statements not executing in order? (defvar within a let statement)语句没有按顺序执行? (在 let 语句中的 defvar)
【发布时间】:2011-09-27 18:09:34
【问题描述】:

我已尝试将其简化为最小示例。代码运行没有错误,产生预期的输出。但它给了我一个警告,我的第一个变量是未定义的。似乎 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”的内容:

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 还没有定义呢?

【问题讨论】:

  • 您应该始终用耳罩包围词汇变量。您应该称它们为 var1arr1。否则你会遇到麻烦。而且我不想读你的代码。
  • 不知道你说的耳罩是什么意思?我只调用变量是因为我希望这个最小的工作示例清晰。 [...编辑掉了等等等等...]。编辑:我查了一下耳罩。谢谢你的建议。我遇到的问题似乎是a)实际上我的所有变量都需要是全局的(“词法”?),或者b)我只想将所有内容包装在巨大的嵌套let语句中。有没有好的中间立场?
  • 耳罩是变量名周围的星星。毫无疑问,您之前偶然发现了它们。您不必相信我的话,而是阅读真正专业人士 (Nikodemus) 的意见:random-state.net/log/3498750808.html
  • 哦,是的,我看到耳罩很好,但如果这些变量如此特殊,它会让我觉得我太多了......再次感谢您的建议。
  • whoplisp 有点困惑。按照惯例,special 变量(由defvar 声明)获得耳罩,而不是词法变量(由let 声明)。这只是一个约定,所以如果你不喜欢它,你不必遵循它,但如果你需要阅读其他 Lisp 程序员编写的代码,则值得了解。

标签: common-lisp let


【解决方案1】:

查看 Hyperspec 中的documentation for defvar

如果defvardefparameter 表单显示为顶级表单,编译器必须识别出名称已被宣布为special

这意味着(SBCL 似乎就是这种情况)如果defvar 以非顶级形式出现,则编译器无需识别该名称已被声明。那么为什么你的defvars 没有被编译为顶级表单呢?请参阅section 3.2.3.1, Processing of Top Level Forms(第 6 点)以获取答案:围绕您的代码的let 会导致它被编译为非顶级表单。

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


像这样。使用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"))

【讨论】:

  • 好的,谢谢,这是有道理的。我并没有真正“得到”顶级表格,但我想我明白......
  • 这是否意味着我不能使用 "let" 构造,所以我必须写 "(defvar input (open "input.lisp"))"?
  • 查看代码。如果您愿意,可以使用let,但在这种情况下with-open-file 更好。
  • 谢谢。我想使用宏来根据输入定义每个变量,这就是为什么 defvars 首先出现在 let 语句中的原因。对此有何建议?我应该先定义变量并让宏设置每个变量吗?
  • 这将是一种方法,或者(取决于您正在做什么)您可以让宏生成整个事情:defvarsetf 等等。
猜你喜欢
  • 2014-03-05
  • 1970-01-01
  • 1970-01-01
  • 2018-02-18
  • 2014-08-23
  • 2021-10-17
  • 2012-05-28
  • 2023-04-04
  • 2017-08-16
相关资源
最近更新 更多