我正在尝试使用SLIME和OpenMCL(好吧,现在称为CCL)在OS X上运行Paul Graham的ANSI Common Lisp的光线跟踪代码.在该代码中,有一个常量定义,其值是一个结构,当我在任何使用该常量的函数上调用slime-compile-and-load-file或slime-compile-defun时,我收到一条错误消息:
没有为#S定义MAKE-LOAD-FORM方法(POINT:X 0:Y 0:Z 200)[类型SIMPLE-ERROR的条件]
我发现了一篇解释复杂性的帖子,另一篇解释了它,但是需要在代码中添加哪些内容来协商OpenMCL的这个方面?
当STRUCTURE-OBJECTs(以及一些其他类型的对象)显示为文字时,COMPILE-FILE处理的代码中的常量对象,COMPILE-FILE需要知道如何在加载生成的二进制文件时安排"等效"对象已创建."等价"有许多可能的定义:有时,加载对象的组件与其他对象共享结构很重要,有时初始化以某种方式发生是很重要的,有时这些都不重要.为了确定如何重新创建常量对象,COMPILE-FILE调用泛型函数MAKE-LOAD-FORM; 应该在任何CL参考或教程中描述此行为.(参考或教程也应注意实施可以'
(defmethod make-load-form ((p point) &optional env) (declare (ignore env)) (make-load-form-saving-slots p))
请注意,必须在编译时定义该方法,以便COMPILE-FILE可以调用它来确定如何保存常量POINT对象.
这些都不是特定于CCL的.可能是哪些事物是不变的,文字对象以及哪些事物不是.
在代码中:
(defconstant a-point (make-point :x 0 :y 0 :z 200)) (defun return-a-point () a-point)
允许(但不是必需)编译器在函数RETURN-A-POINT中用A-POINT的值替换它的引用.(如果编译器这样做,那意味着正在编译的代码中有一个文字/常量POINT对象,并且COMPILE-FILE需要调用MAKE-LOAD-FORM来确定如何保存和加载对象;如果是编译器不执行此替换,因此在此示例中不需要调用MAKE-LOAD-FORM.)
实现是否进行此类替换取决于实现.规范还没有说明是否在编译时,加载时或两者都评估DEFCONSTANT表单中的值表单,并且注意必须(必须由用户)执行以确保表达式始终求值为相同的价值.
CCL通常尝试在编译时评估DEFCONSTANT值表单,并且相当积极地将命名常量的值替换为对它们的引用; 在某些情况下,这意味着必须定义常量值的类上的MAKE-LOAD-FORM方法.其他实现可能不太愿意对某些类型的对象进行这种替换.这两种策略都是正确的,并且可移植代码无法假设正在遵循哪些策略(尽管据称可移植代码肯定会做出这样的假设.)
对DEFCONSTANT定义的事物的不同处理似乎是这种事情的最可能原因(意外调用MAKE-LOAD-FORM,没有人愿意定义).可以通过以下方式以便携式方式避免某些问题:
(defconstant a-point (make-point :x 0 :y 0 :z 200)) (defun return-a-point () (load-time-value (symbol-value 'a-point)))
这将具有类似的效果,只是允许想要这样做的实现(如CCL那样)进行常量替换,但使用LOAD-TIME-VALUE将确保仅在加载时评估常量值(和不参与MAKE-LOAD-FORM.)