【问题标题】:Common Lisp: Controlling macro expansion timeCommon Lisp:控制宏扩展时间
【发布时间】:2012-12-26 23:34:03
【问题描述】:

我正在使用 common lisp,发现自己经常输入以下形式的插槽定义:

(name :initarg :name :accessor name)

所以我想制作一个宏来加快速度。我想出了以下几点:

(defmacro quickslot (name)
`(,name :initarg ,(intern (string-upcase name) "KEYWORD") :accessor ,name))

毫无疑问,这是一个肮脏的黑客,但很实用。或者我是这么想的。当我试图运行我的代码时,我遇到了一个障碍:因为 defclass 是一个宏,所以传递给它的参数是未经计算的。这意味着,而不是看到

(x :initarg :x :accessor x)

看到

(quickslot x)

这当然是一个错误信号。

在我看来,答案是以某种方式控制宏扩展的顺序,以确保在 defclass 之前扩展快速槽。这让我想到了我的问题:如何做到这一点?或者,如果您对我最初的难题有不同的解决方案,那也不会不受欢迎。

【问题讨论】:

  • 忠告:不要通过微不足道的宏“改进”基本的 Common Lisp 语法,而是使用编辑器自动插入您认为冗长的代码。 defclass 的语法是初学者编写的宏的特别受害者。
  • @hans23 非常好的建议。
  • 另一个可能的选择是使用defstruct 而不是defclass。它更轻量级,除了定义initargs和accessors外,它还定义了一个可读的打印方法。

标签: macros common-lisp clos


【解决方案1】:

这真的不值得一个宏。宏通常将 Lisp 源代码作为其输入。

相反,您可以只使用一个函数。来自Practical Common Lisp, Ch.24

(defun as-keyword (sym) (intern (string sym) :keyword))

(defun slot->defclass-slot (spec)
  (let ((name (first spec)))
    `(,name :initarg ,(as-keyword name) :accessor ,name)))

然后你可以做类似的事情(再次改编自PCL):

(defmacro my-defclass (name slots)
  `(defclass ,name ()
     ,(mapcar #'slot->defclass-slot slots)))

【讨论】:

    【解决方案2】:

    不,你不能这样做。不过,您可以在 defclass 周围编写一个宏(这对您的快速槽有一些特殊的语法)。

    【讨论】:

      【解决方案3】:

      您可以完全不同地解决问题,并提出一个阅读器宏,指示阅读器在其后面的代码上调用macroexpand,这将比在类中声明槽的唯一目的更通用.但是完整的解决方案会有些复杂,因为您必须考虑到读者的许多特点和要求,但是,即使是像这样丑陋的东西也可以完成这项工作:

      (defmacro quickslot (name)
      `(,name :initarg ,(intern (string-upcase name) "KEYWORD") :accessor ,name))
      
      (macroexpand '(defclass test-class ()
                     (#.(macroexpand '(quickslot some-slot)))))
      

      所以,你需要做的是类似于 #.(macroexpand ...) 的别名


      然后……给你:

      (set-macro-character
       #\{
       #'(lambda (str char)
           (declare (ignore char))
           (let ((*readtable* (copy-readtable *readtable* nil))
                 (reading-p t))
             (set-macro-character
              #\}
              #'(lambda (stream char)
                  (declare (ignore char stream))
                  (setf reading-p nil)))
             (loop for exp = (read str nil nil t)
                while reading-p
                collect (macroexpand exp)))))
      
      (read-from-string "'(defclass test-class ()
                     {(quickslot some-slot)
                     (quickslot some-other-slot)})")
      '(DEFCLASS TEST-CLASS NIL
                 ((SOME-SLOT :INITARG :SOME-SLOT :ACCESSOR SOME-SLOT)
                  (SOME-OTHER-SLOT :INITARG :SOME-OTHER-SLOT :ACCESSOR
                   SOME-OTHER-SLOT)))
      

      :)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-12-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-22
        • 2013-07-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多