使用SBCL时,我遇到的问题是,当lisp代码定义一个字符串常量时,通过ASDF定义的系统不会加载.这是代码:
constants.lisp
(defconstant A 1.0)
(defconstant B "B")
Run Code Online (Sandbox Code Playgroud)
simple.asd
(defsystem :simple
:components ((:file "constants")))
Run Code Online (Sandbox Code Playgroud)
在加载时
(asdf:load-system "simple")
Run Code Online (Sandbox Code Playgroud)
我收到以下错误(输出已经缩短了一点):
* (asdf:load-system "simple")
; compiling file "/Users/.../constants.lisp"
; compiling (DEFCONSTANT A ...)
; compiling (DEFCONSTANT B ...)
; /Users/.../constants-TMP.fasl written
; compilation finished in 0:00:00.003
debugger invoked on a DEFCONSTANT-UNEQL in thread
#<THREAD "main thread" RUNNING {1002BFEA93}>:
The constant B is being redefined (from "B" to "B")
Run Code Online (Sandbox Code Playgroud)
clisp,ccl和abcl没有出现错误.另外,通过加载文件
(load "constants.lisp")
Run Code Online (Sandbox Code Playgroud)
工作良好.
我正在使用
SBCL 1.2.14,ASDF 3.1.3,MacOS
谢谢你的任何提示,
奥利弗
规范defconstant告诉我们:
但是,如果尝试使用另一个运算符为符号赋值,或者使用后续的defconstant将其分配给不同的值,则后果是未定义的.
这里重要的一点是不同的:根据哪个比较?
如果在执行defconstant时存在由name命名的变量的任何绑定,或者该值不是eql到initial-value的值,则结果是未定义的.
比较由eql完成.
SBCL编译您的文件然后加载结果(xxx-TMP.fasl文件),并且对于该特定实现,defconstant因此在同一环境中对表单进行两次评估.编译器在编译期间不需要实际评估表单(它可以在内部以某种方式声明它,以便可以内联进一步使用常量),但这是一种有效的编译策略.
这里,由于编译环境和加载环境是相同的,并且因为字符串的两个出现不相同(不是eq),所以发出错误信号.如果您碰巧使用相同版本的SBCL解释器的另一个实例加载FASL文件,它将不会给您这个错误.
不要重新发明轮子,使用 alexandria:define-constant,这允许指定值在哪个测试函数不变.这是一个更复杂的版本:
(defmacro defconst (symbol value)
`(defconstant ,symbol
(or (and (boundp ',symbol)
(symbol-value ',symbol))
,value)))
Run Code Online (Sandbox Code Playgroud)
在这里,扩展defconstant仍然是顶级形式,我们永远不会用不eql适合它的东西重新定义它.
defvar在部署代码之前使用并且不再担心它(通常需要在开发期间更改常量).