根据practical common lisp引用中提供的示例,我定义了一个宏来创建一个类。
(defmacro define-class (class-name class-slot)
`(defclass ,class-name ()
,(mapcar #'slot->defclass-slot class-slot))))
函数slot-> declass-slot采用单个参数并生成描述类中槽的标准行。代码如下:
(defun slot->defclass-slot (spec)
`(,spec :initarg ,(as-keyword spec) :accessor ,spec :initform 0))
例如,
(slot->defclass-slot 'nom)
(NOM :INITARG :NOM :ACCESSOR NOM :INITFORM 0)
当我按照以下方式创建一个类'模型'时,所有这些工作都很好:
(define-class model (nom id))
但是假设我定义了一个参数。
(defparameter *test* '(nom id))
(define-class model *test*)
然后,代码以错误结束:
The value *TEST* is not of type LIST.
有什么问题?
答案 0 :(得分:4)
您的define-class
宏未评估其class-slots
参数。
您可以像这样“修复”您的代码:
(defmacro define-class (class-name class-slots)
`(eval
`(defclass ,',class-name ()
,@(mapcar #'slot->defclass-slot ,class-slots))))
(macroexpand-1 '(define-class model '(nom id)))
(defparameter *test* '(nom id))
(define-class model *test*)
请注意,您现在必须将第二个参数引用到define-class
。
另请注意,您现在正在使用eval
(在这种情况下有充分理由)。
最后请注意,我非常怀疑你真的想要这样做。你可能不需要这种活力水平,而你只是没有充分理由使你的生活复杂化。
例如,如果您只想获取班级列表(使用*test*
变量),则应使用MOP代替。
实际上,您可以将宏扩展为功能 ensure-class
:
> (mop:ensure-class 'foo :direct-slots '((:name a)))
#<STANDARD-CLASS FOO>
但这依赖于一种无耻的假设,即您的实施是MOP-compliant。
答案 1 :(得分:1)
(defparameter *test* '(nom id)) (define-class model *test*)
您不应该尝试这样做,原因与您从未尝试过的相同:
(with-open-file '(...)
...)
宏的要点是不评估参数,以便您可以对它们执行某些操作。您可以做的事情,如果由于某种原因 ,需要宏版本和非宏版本,就是根据a来定义宏功能。函数,然后在需要宏时将函数包装在宏中。例如,(对于非特别强大的) with-open-file ):
(defun %with-open-file (filename function &rest args)
(let ((file (apply 'open filename args)))
(prog1 (funcall function file)
(close file))))
(defmacro with-open-file ((var filename &rest args) &body body)
`(%with-open-file ,filename
(lambda (,var) ,@body)
,@args))
然后您可以根据需要使用宏版本,并在需要时使用函数版本。但是,在您的情况下,这不是一个完美的解决方案,因为您正在扩展到另一个宏调用。