代码之家  ›  专栏  ›  技术社区  ›  Paul Nathan

带defclass的defmacro

  •  7
  • Paul Nathan  · 技术社区  · 14 年前

    我有一个普通的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自动作为slots出来。

    但是,当我去做一个 macroexpand-1 我收到了读者的错误。

    第一个是“冒号后非法终止字符…”,然后继续。

    SBC1.1.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))):
    

    两年半后。

    我在别处的野外发现了六个月前的密码。虽然我很荣幸,但它也提醒我更新这个。

    如果你喜欢这个想法,我把这个代码放在: https://github.com/pnathan/defobject . 和以前一样,它的目标是生成具有最少重复类型的CLOS类。类似的系统被称为defclass-star。建议利益相关方对两者进行审查。

    4 回复  |  直到 11 年前
        1
  •  5
  •   Rainer Joswig Michael Fox    14 年前

    您不能将宏放入您想要的代码中。 在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

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

    一本基本的口齿不清书可能会有所帮助。阅读Lisp语法。

    :,foo
    

    上面没有意义。

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

    如果要创建符号,需要调用intern或make-symbol。

    解决方案

    编写一个my-defclass宏,该宏允许使用较短的语法并扩展为defclass。已经有defclass*宏在库中执行类似的操作。

        2
  •  3
  •   6502    14 年前

    我通常用这种东西

    (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))
    

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

        3
  •  2
  •   Vatine    14 年前

    正如雷纳所说,宏只在可以接受函数调用的地方进行扩展。

    我所做的是限制我在定义插槽时实际需要键入的样板,即有两个编辑器宏,一个用于带读卡器的插槽,另一个用于带访问器的插槽(我很少有带单独编写器的插槽,但如果有,我必须手动编写)。

        4
  •  0
  •   Nathan Davis    14 年前

    宏从上到下递归展开。在您的示例中, defclass 宏首先展开--在 make-slot 宏。扩展的代码 防御阶级 没有预料到 制作插槽 宏——它需要一个槽定义。

    正如其他人所建议的,读者错误是因为 `:,symbol 不是有效的lisp。但首先只需将关键字传递到宏中就足够容易了。