3

前段时间,对于一个基于动物园的小示例,我编写了一个基类 ANIMAL,一些子类 CAT、MOUSE 等。一个通用方法 FEED 采用 ANIMAL 参数和一些专门针对每个 ANIMAL 子类型的方法。

在编写了第二和第三类方法对之后,我意识到我一遍又一遍地写同样的东西,并决定编写一个宏 DEF-ANIMAL-SUBCLASS,它扩展为一个定义新子类和适当方法的 PROGN。

然后我意识到我刚刚为我的用户提供了一种定义他们自己的 ANIMAL 子类型的方法,他们可能会觉得这很有用!然而,虽然他们可能只是在运行的图像中这样做,但我没有办法保存他们的新 ANIMAL 类型,以便在重新启动图像时,将为他们重新创建任何新的 ANIMAL 类型(无需他们重新评估宏)。

有没有传统的方法可以做到这一点?

这是不应该做的事情吗?

任何提示将不胜感激!

干杯,

4

3 回答 3

7

如果你用宏定义你的动物类,那么你可以让宏记录源代码。

示例(在 LispWorks 中工作):

例如,我们可以使用 alist 将源代码存储在类分配的插槽中。或者,您可以只使用一个简单的全局变量。

(defclass animal ()
  ((source :allocation :class :initform nil)))

上面有一个插槽,它应该指向一个类名和源代码的列表。

(defmacro def-animal-class (&whole code name feeding )
  `(progn
     (defclass ,name (animal) ())
     (defmethod feed ((animal ,name)) ,@feeding)
     (let ((item (assoc ',name
                        (slot-value (class-prototype (find-class 'animal))
                                    'source))))
       (if item
           (setf (cdr item) ',code)
         (setf (slot-value (class-prototype (find-class 'animal))
                           'source)
               (list (cons ',name ',code)))))
     ',name))

生成的代码是什么样的?

CL-USER > (pprint (macroexpand-1 '(def-animal-class cat ((print "feeding a cat")))))

(PROGN
  (DEFCLASS CAT (ANIMAL) NIL)
  (DEFMETHOD FEED ((ANIMAL CAT)) (PRINT "feeding a cat"))
  (LET ((ITEM (ASSOC 'CAT (SLOT-VALUE (CLASS-PROTOTYPE (FIND-CLASS 'ANIMAL))
                                      'SOURCE))))
    (IF ITEM
        (SETF (CDR ITEM) '(DEF-ANIMAL-CLASS CAT ((PRINT "feeding a cat"))))
      (SETF (SLOT-VALUE (CLASS-PROTOTYPE (FIND-CLASS 'ANIMAL)) 'SOURCE)
            (LIST (CONS 'CAT '(DEF-ANIMAL-CLASS CAT ((PRINT "feeding a cat"))))))))
  'CAT)

使用它:

CL-USER 75 > (def-animal-class cat ((print "feeding a cat some more")))
CAT

CL-USER 76 > (cdr (first (slot-value (class-prototype (find-class 'animal))
                                     'source)))

(DEF-ANIMAL-CLASS CAT ((PRINT "feeding a cat some more")))

因此,每当使用宏时,都会记录最后一个源代码DEF-ANIMAL-CLASS。然后,您可以例如将代码写入文件:

(with-open-file (s "~/animals.sexp" :direction :output :if-exists :supersede)
  (pprint (slot-value (class-prototype (find-class 'animal)) 'source) s))

一个简单的READ把它带回来。

于 2012-05-16T20:02:20.090 回答
5

执行此操作的常规方法是使用数据库来存储您的动物子类。选择一个,连接 CLSQL 并让它以您可以解释回它们各自定义的格式存储动物记录。

根据规模和部署情况,您也可能只是在平面文件中处理它。

也就是说,除了定义一个新的子类和方法之外,还要将def-animal-subclass它们的语句序列化def...到一个单独的.lisp文件中。然后,您的程序将在处理其配置时加载该文件。(请务必仔细考虑一下。例如,如果您的用户定义了一个已经存在的动物子类会发生什么?)看看 Emacs 如何存储一些想法的自定义。

于 2012-05-16T16:30:47.483 回答
3

Common Lisp 是一种基于图像的语言,因此,除了 Inaimathi 的答案中给出的解决方案之外,您还可以只保存图像和所有用户定义的类(以及其他状态,除了网络连接等短暂的东西。 ) 将在那里,如果你重新启动它。

如何做到这一点取决于您的 CL 实现,因此您必须检查其文档。CCL 使用ccl:save application、 SBCL sb-ext:save-lisp-and-die、 CLISPext:saveinitmem等。

当然,选择哪种方法(Inaimathi 建议的方法或保存图像)取决于您的应用程序和需求,因为每种方法都有不同的优点和缺点。

于 2012-05-16T19:35:47.157 回答