【问题标题】:defmacro with defclass带有 defclass 的 defmacro
【发布时间】:2011-04-21 14:02:44
【问题描述】:

我有一个 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 的 make-slot 时,男孩你好,我收到了阅读器错误。

第一个是“冒号后的非法终止字符......”然后它一直在继续。

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

两年半之后。

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

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

【问题讨论】:

    标签: macros lisp common-lisp


    【解决方案1】:

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

    正如其他人所建议的,阅读器错误是因为`:,symbol 不是有效的 Lisp。但是首先将关键字传递到宏中就很容易了。

    【讨论】:

    • 有趣。我不知道 defclass 本身就是一个宏。我想我会听从 Rainer 的建议,买一本 Lisp 纸质书。
    • @Paul Nathan,所有的 DEFSOMETHING 通常都是宏。
    【解决方案2】:

    我通常使用这样的东西

    (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】:

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

      为了限制在定义插槽时实际需要输入的样板,我所做的是有两个编辑器宏,一个用于带有读取器的插槽,一个用于带有访问器的插槽(我很少有单独的插槽作家,但如果我这样做了,我就不得不手写了)。

      【讨论】:

        【解决方案4】:

        您不能将宏放在您想要的代码中。阅读 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 书可能会有所帮助。阅读 Lisp 语法。

        :,foo
        

        上面没有意义。

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

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

        解决方案

        编写一个允许更短语法并扩展为 DEFCLASS 的 MY-DEFCLASS 宏。库中已经有 DEFCLASS* 宏在做类似的事情。

        【讨论】:

        • 嗯。我认为 - 显然是错误的 - 宏会以替换方式扩展;因此,defclass 会看到插槽列表。
        • @Paul Nathan,Lisp 应该如何确定 MAKE-SLOT 是宏而不是插槽的名称? DEFCLASS 语法很明确,它需要一个插槽列表。它不期望一个函数计算为一个槽列表。
        • 啊,我想我的主要错误是我对宏的扩展方式和时间的思考。
        • 我觉得关键是defclass会在make-slot之前展开(因为先遇到defclass)。因此传递给defclass 的数据将包括未扩展的make-slot 表单。但是defclass 并不期待那里有一个表格,它期待一个插槽定义。
        • @Nathan Davis。对,顶层表单是一个扩展的 DEFCLASS 表单。 DEFCLASS 是一个宏。这样的宏本身可以扩展表单,但在这种情况下它不能。它返回一个带有多个子表单的新表单。只需对上述形式进行宏扩展 (macroexpand '(defclass ...)) 并查看结果。 DEFCLASS 不扩展槽形式,它需要描述槽的真实列表。评估是由内而外的。但宏观扩张是从外部开始的。生成的表单也可能有需要扩展的地方,但在这种情况下也不是这样。插槽已被处理。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多