7

我正在尝试使用 SLIME 和 OpenMCL(现在称为 CCL)在 OS X 上运行来自 Paul Graham 的ANSI Common Lisp的光线追踪代码。在该代码中,定义了一个常量,其值为结构,当我在任何使用该常量的函数上调用slime-compile-and-load-fileslime-compile-defun时,我收到一条错误消息:

没有为#S(POINT :X 0 :Y 0 :Z 200) [SIMPLE-ERROR 类型的条件] 定义 MAKE-LOAD-FORM 方法

我发现了一篇解释复杂性的帖子和另一篇感叹它的帖子,但是需要在代码中添加什么来协商 OpenMCL 的这一方面?

4

1 回答 1

8

当 STRUCTURE-OBJECT(和一些其他类型的对象)在 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 。)

于 2008-12-07T10:26:06.087 回答