我正在尝试使用SLIME和OpenMCL(好吧,现在称为CCL)在OS X上运行Paul Graham的 ANSI Common Lisp 形式的光线跟踪代码。在该代码中,有一个常量定义,其值是一个结构,当我在任何一个上调用 slime-compile-and-load-file 或 slime-compile-defun 时使用常量的函数,我收到一条错误信息:
未定义MAKE-LOAD-FORM方法 对于#S(POINT:X 0 :Y 0 :Z 200)[类型的条件 SIMPLE-ERROR]
我发现a post解释了并发症,并another one感叹它,但是需要在代码中添加哪些内容来协商OpenMCL的这一方面?
答案 0 :(得分:8)
当STRUCTURE-OBJECT(和其他一些类型的对象)显示为文字时,COMPILE-FILE处理的代码中的常量对象,COMPILE-FILE需要知道如何安排,当加载生成的二进制文件时,创建“等效”对象。 “等价”有许多可能的定义:有时,加载对象的组件与其他对象共享结构很重要,有时初始化以某种方式发生是很重要的,有时这些都不重要。为了确定如何重新创建常量对象,COMPILE-FILE调用泛型函数MAKE-LOAD-FORM;应该在任何CL参考或教程中描述此行为。 (参考或教程还应注意,实现不能定义适用于STRUCTURE-CLASS或STANDARD-CLASS的所有实例的默认MAKE-LOAD-FORM方法,并且还应注意MAKE-LOAD-FORM-SAVING -SLOTS是一种方便的函数,可用于MAKE-LOAD-FORM方法,用于初始化不需要复杂的对象,例如:
(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。)