有关保存宏/函数调用的任何提示?

时间:2012-05-16 15:10:13

标签: lisp common-lisp

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

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

然后我意识到我刚给了我的用户一种定义他们自己的ANIMAL子类型的方法,他们可能觉得这些子类很有用!然而,虽然他们可能只是在正在运行的图像中执行此操作,但我没有办法保存其新的ANIMAL类型,以便在重新启动图像的情况下,将为它们重新创建任何新的ANIMAL类型(没有他们必须重新评估宏。)

有传统方法吗?

是不是应该做的事情?

任何提示都会感激不尽!

干杯,

P

3 个答案:

答案 0 :(得分: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将它带回来。

答案 1 :(得分:5)

这样做的传统方法是使用数据库来存储动物子类。选择一个,挂钩CLSQL并让它以一种格式存储动物记录,你可以将它们解释回各自的定义。

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

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

答案 2 :(得分:3)

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

如何执行此操作取决于您的CL实现,因此您必须检查其文档。 CCL使用ccl:save application,SBCL sb-ext:save-lisp-and-die,CLISP ext:saveinitmem等。

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