defmacro与defclass

时间:2010-10-06 00:03:24

标签: macros lisp common-lisp

我在Common Lisp中有一个课程:

(defclass my-cool-class()
  ((variable1
    :initarg :variable1
    :accessor variable1
    :initform (error "Must supply value to variable1"))
   (variable2
    :initarg :variable2
    :accessor variable2
    :initform (error "Must supply value to variable2"))

我想创建一个可以简化输入

冗余的宏
(defmacro make-slot (slot-name)
  `(slot-name 
     :initarg :,slot-name
     :accessor :,slot-name
     :initform (error "Must supply value")))

最终我想(defclass my-cool-class()(make-slots'(foo bar baz))并自动将foo,bar和baz作为插槽。

但是,当我去做macroexpand-1制作插槽时,男孩我怎么会收到读者错误。

第一个是“结肠后非法终止性格......”然后继续前进。

SBCL 1.0.37。

编辑:示例在系统上语法正确,我在复制之前做了一些编辑。


六个月后 -

(defun build-var (classname var)
  (list var 
        :initform nil
        :accessor (intern (concatenate 'string (string classname) "-" 
                                       (string var)))
        :initarg (intern (string var) :keyword)))

(defun build-varlist (classname varlist)
   (loop for var in varlist 
         collect (build-var classname var)))


(defmacro defobject (name &rest varlist)
  "Defines a class with a set of behavior. 
   Variables are accessed by name-varname.

   (defobject classname v1 v2 v3)
  "
  `(defclass ,name ()
     ,(build-varlist name varlist))):

两年半之后。

我在其他地方发现了6个月大的代码。虽然我受宠若惊,但它也提醒我更新这个。

如果您喜欢这个想法,我会将此代码保留在:https://github.com/pnathan/defobject。和以前一样,它的目标是以最少的重复输入生成CLOS类。存在类似的系统,称为DEFCLASS-STAR。建议有兴趣的人士查看两者。

4 个答案:

答案 0 :(得分:5)

您不能将宏放在您想要的代码中。在CLHS中阅读构造的语法。

例如你不能这样做:

(defun foo (make-arg-list 'a 'b) a b)

DEFUN需要一个arglist,而不是一个创建arglist的函数。

Lisp扩展了宏,预计会出现Lisp格式。在预期其他列表(例如插槽列表)的地方,Lisp不会宏扩展。

类似的DEFCLASS需要一个插槽列表,而不是一个创建插槽列表的函数。 类似于插槽列表,DEFCLASS希望每个插槽都是名称或描述插槽的列表。

参见DEFCLASS的语法: http://www.lispworks.com/documentation/HyperSpec/Body/m_defcla.htm

你不能把逗号放在你想要的地方。

Probabaly一本基本的Lisp书可能有所帮助。阅读Lisp语法。

:,foo

以上没有意义。

逗号运算符将项放入反引号列表中。它不会将项目放入符号中。

如果要创建符号,则需要调用INTERN或MAKE-SYMBOL。

<强>解决方案

编写一个MY-DEFCLASS宏,允许更短的语法并扩展到DEFCLASS。已有DEFCLASS *宏在库中执行类似的操作。

答案 1 :(得分:3)

我通常使用这样的东西

(defmacro mydefclass (name fields)
  `(defclass ,name ()
     ,(let ((res nil))
        (dolist (f fields)
          (let* ((fname (symbol-name f))
                 (kw (intern fname :keyword)))
            (push `(,f :accessor ,kw
                       :initarg ,kw
                       :initform (error
                                  ,(format NIL "Must supply value to ~a"
                                           fname)))
                  res)))
        (nreverse res))))

然后

(mydefclass foo (x y z))

添加一些逻辑来处理对自定义插槽的需求非常容易(例如,当字段是列表而不是符号时,您可以在扩展中逐字复制输入)

答案 2 :(得分:2)

正如Rainer所说,只有在函数调用可以接受的情况下才会扩展宏。

我在限定插槽时需要实际打字的锅炉板的做法是,有两个编辑器宏,一个用于带读卡器的插槽,另一个用于带有存取器的插槽(我很少有单独的插槽作家,但如果我这样做,我必须手工编写)。

答案 3 :(得分:0)

宏从上到下递归扩展。在您的示例中,defclass宏首先展开 - 在您的make-slot宏之前。扩展defclass的代码并不期望未扩展的make-slot宏 - 它期望一个插槽定义。

正如其他人所建议的那样,读者错误是因为`:,symbol无效的Lisp。但是,首先将关键字传递给宏很容易。