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

如何在LISP中使用参数而不是列表调用宏?,lisp,common-lisp,Lisp,Common Lisp,根据参考中提供的示例,我定义了一个宏来创建一个类,如下所示 (defmacro define-class (class-name class-slot) `(defclass ,class-name () ,(mapcar #'slot->defclass-slot class-slot)))) 函数slot->declass slot接受一个参数并生成一个标准行来描述类中的一个slot。代码如下: (defun slot->defclass-slot (spec)

根据参考中提供的示例,我定义了一个宏来创建一个类,如下所示

(defmacro define-class (class-name class-slot)
  `(defclass ,class-name ()
     ,(mapcar #'slot->defclass-slot class-slot)))) 
函数slot->declass slot接受一个参数并生成一个标准行来描述类中的一个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.

怎么了?

您的
定义类
宏没有计算其
类槽
参数。 您可以这样“修复”代码:

(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*)
请注意,现在必须引用literal第二个参数来定义类

还要注意,您现在使用的是
eval
(在本例中,这是一个很好的理由)

最后请注意,我严重怀疑您是否真的想这样做。很可能你不需要这种程度的活力,你只是无缘无故地让你的生活变得复杂

例如,如果您只想获取类槽列表(使用
*test*
变量),则应使用。 实际上,您可以将宏扩展到以下函数:

>(mop:sure类'foo:directslots'(:name a)))
#

但这有赖于一种厚颜无耻的假设,即您的实现是。

您的
定义类
宏不会计算其
类槽
参数。 您可以这样“修复”代码:

(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*)
请注意,现在必须引用literal第二个参数来定义类

还要注意,您现在使用的是
eval
(在本例中,这是一个很好的理由)

最后请注意,我严重怀疑您是否真的想这样做。很可能你不需要这种程度的活力,你只是无缘无故地让你的生活变得复杂

例如,如果您只想获取类槽列表(使用
*test*
变量),则应使用。 实际上,您可以将宏扩展到以下函数:

>(mop:sure类'foo:directslots'(:name a)))
#
但这有赖于一种厚颜无耻的假设,即您的实现是成功的

你不应该尝试这样做,原因与你从未尝试过的原因相同:

(with-open-file '(...)
  ...)
宏的要点是不要为了处理参数而计算参数。相反,如果出于某种原因需要宏版本和非宏版本,则可以用函数定义宏功能,然后在需要宏时将函数包装在宏中。例如,(对于不是特别健壮的)打开文件的

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

你不应该尝试这样做,原因与你从未尝试过的原因相同:

(with-open-file '(...)
  ...)
宏的要点是不要为了处理参数而计算参数。相反,如果出于某种原因需要宏版本和非宏版本,则可以用函数定义宏功能,然后在需要宏时将函数包装在宏中。例如,(对于不是特别健壮的)打开文件的


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

除最后一个命令外,所有命令都正常运行。最后,我在DEFCLASS模型中得到以下消息
,插槽名称:INITARG是一个关键字
。为了回答您的问题,我之所以这么做是因为在阅读cvs文件之前,我不知道类模型的确切属性是什么。因此,我不仅对类定义,而且对以后在网页中的显示,都做了某种程度的通用。我认为类定义已经符合MOP。如果宏定义中没有@,它会更好地工作。谢谢。由于提到了元对象协议,您可以使用所有命令动态定义类,但最后一个命令可以正常运行。最后,我在DEFCLASS模型中得到以下消息
,插槽名称:INITARG是一个关键字
。为了回答您的问题,我之所以这么做是因为在阅读cvs文件之前,我不知道类模型的确切属性是什么。因此,我不仅对类定义,而且对以后在网页中的显示,都做了某种程度的通用。我认为类定义已经符合MOP。如果宏定义中没有@,它会更好地工作。谢谢。既然提到了元对象协议,你就可以用。好主意。我将宏重新定义为函数,并编辑一个新宏,如下所示
(defmacro%define class(name slots)`(define class,name,slots))
。然后,命令:
(%define class'model*test*)
运行,但它返回列表:
(DEFCLASS model NIL(NOM:INITARG:NOM:ACCESSOR NOM:INITFORM 0)(IDD:INITARG:IDD:ACCESSOR IDD:INITFORM 0))
。这正是我所期望的,但不是列表形式。不知何故,我有一种感觉,我永远不会在正确的水平结束,我应该使用额外的评估命令。我可能误解了宏中的某些内容。我不知道如何克服这一问题。@Xaving,正如我在回答中所说,“在您的情况下,这不是一个完美的解决方案,因为您正在扩展到另一个宏调用。”这种方法在这里不会真正起作用,因为您需要的最后一步是宏调用,而不是函数调用。好主意。我将宏重新定义为函数,并编辑一个新宏,如下所示
(defmacro%define class(name slots)`(define class,name,slots))
。然后,运行命令:
(%define class'model*test*)
,但它返回列表:
(DEFCLASS model>)
(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))