不久之前,对于一个基于动物园的小例子,我写了一个基类ANIMAL,一些子类CAT,MOUSE等.一个通用方法FEED采用ANIMAL参数和一些专门用于每个ANIMAL子类型的方法.
在编写了第二和第三类之后,方法对我意识到我一遍又一遍地写同样的东西,并决定编写一个宏DEF-ANIMAL-SUBCLASS,它扩展为PROGN,定义了新的子类和适当的方法.
然后我意识到我刚刚给了我的用户一种定义他们自己的ANIMAL子类型的方法,他们可能觉得这些子类很有用!然而,虽然他们可能只是在正在运行的图像中执行此操作,但我没有办法保存其新的ANIMAL类型,以便在重新启动图像的情况下,将为它们重新创建任何新的ANIMAL类型(没有他们必须重新评估宏).
有这样做的传统方式吗?
这是不应该做的事情?
任何提示都会感激不尽!
干杯,
P
如果使用宏定义动物类,则可以让宏记录源代码.
示例(适用于LispWorks):
我们可以使用alist将源代码存储在类分配的槽中.或者,您可以使用一个简单的全局变量.
(defclass animal ()
((source :allocation :class :initform nil)))
Run Code Online (Sandbox Code Playgroud)
上面有一个插槽,应该指向类名和源代码的列表.
(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))
Run Code Online (Sandbox Code Playgroud)
生成的代码是什么样的?
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)
Run Code Online (Sandbox Code Playgroud)
使用它:
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")))
Run Code Online (Sandbox Code Playgroud)
因此,只要使用宏,就会记录最后的源代码DEF-ANIMAL-CLASS.然后,您可以将代码写入文件:
(with-open-file (s "~/animals.sexp" :direction :output :if-exists :supersede)
(pprint (slot-value (class-prototype (find-class 'animal)) 'source) s))
Run Code Online (Sandbox Code Playgroud)
一个简单的READ回归.
这样做的传统方法是使用数据库来存储动物子类.选择一个,连接CLSQL并让它以一种格式存储动物记录,您可以将其解释回各自的定义.
根据规模和部署情况,您也可以在平面文件中处理它.
也就是说,除了定义新的子类和方法之外,还要将def-animal-subclass它们的def...语句序列化为单独的.lisp文件.然后,您的程序将在处理其配置的位置加载该文件.(请务必仔细考虑一下.例如,如果您的用户定义了已存在的动物子类,会发生什么?)看看Emacs如何为某些想法存储自定义.