如何在LISP中使用参数而不是列表调用宏?

时间:2015-02-03 16:43:43

标签: lisp common-lisp

根据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.

有什么问题?

2 个答案:

答案 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))

然后您可以根据需要使用宏版本,并在需要时使用函数版本。但是,在您的情况下,这不是一个完美的解决方案,因为您正在扩展到另一个宏调用。