为何Clozure CL中#.读取宏在CASE使用常量作为键时报错
问题原因
这个问题是ANSI Common Lisp标准对编译期顶层定义的处理规则和不同Lisp实现的默认行为差异导致的。
当你把测试代码作为单个文件编译/加载时,触发报错的完整流程如下:
- 阅读器按顺序读取文件内的每个S表达式
- 编译器处理读取到的表达式时,默认只会将顶层的
defconstant定义安排到生成的目标代码中,仅在加载文件的阶段才执行绑定操作,编译阶段不会默认将这类顶层定义写入当前编译环境 - 当阅读器读到
ecase内的#.+one+时,#.是读取时求值的阅读器宏,会立刻尝试获取+one+的绑定值,此时CCL的编译环境中还没有+one+的绑定,就会抛出未绑定变量的错误。
SBCL可以正常运行的原因是,它对顶层常量定义做了实现层面的扩展,会自动将defconstant的定义同步到编译期环境中,因此读取#.+one+时可以正常拿到值,不会报错。
解决方案
如果要让代码在所有符合标准的Common Lisp实现中都能正常运行,只需要用eval-when包裹常量定义,强制在编译期也执行常量绑定操作,修改后的常量定义部分如下:
(eval-when (:compile-toplevel :load-toplevel :execute) (defconstant +one+ 1) (defconstant +two+ 2))
这个声明会告诉编译器,当前包裹的代码需要同时在编译期、加载期、运行期三个阶段执行,编译阶段+one+和+two+就会完成绑定,后续读取#.+one+的操作就可以正常执行了。
内容的提问来源于stack exchange,提问作者Maris Orbidans
相关产品推荐
相关产品推荐

