Macros Common Lisp:控制宏扩展时间

Macros Common Lisp:控制宏扩展时间,macros,common-lisp,clos,Macros,Common Lisp,Clos,我在使用common lisp时,发现自己经常输入以下表单的插槽定义: (name :initarg :name :accessor name) 所以我想编造一个宏来加速这个过程。我得出了以下结论: (defmacro quickslot (name) `(,name :initarg ,(intern (string-upcase name) "KEYWORD") :accessor ,name)) 一个肮脏的黑客,毫无疑问,但功能。至少我是这么想的。当我试图运行我的代码时,我遇到了一个障

我在使用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)
当然,这是一个错误的信号


在我看来,答案是以某种方式控制宏扩展的顺序,以确保在定义类之前扩展quickslot。这就引出了我的问题:如何才能做到这一点?或者,如果你对我最初的难题有不同的解决方案,那也不会不受欢迎。

不,你不能这么做。不过,您可以围绕
defclass
编写一个宏(这对您的快速运行有一些特殊的语法)。

不,您不能这样做。不过,您可以围绕
defclass
编写一个宏(它对您的快速运行有一些特殊的语法)。

这实际上不值得使用宏。宏通常以文字Lisp源代码作为输入

相反,您可以只使用一个函数。发件人:

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


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

相反,您可以只使用一个函数。发件人:

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


您可以以完全不同的方式处理该问题,并提出一个reader宏,指示读取器对其后的代码调用
macroexpand
,这将比类中slot声明的唯一用途更通用。但是,完整的解决方案可能会有些复杂,因为您必须考虑读者的许多特点和要求,然而,即使是像这样丑陋的东西也可以做到这一点:

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

:)

您可以以完全不同的方式处理问题,并提出一个reader宏,指示读取器对其后面的代码调用
macroexpand
,这将比类中slot声明的唯一用途更通用。但是,完整的解决方案可能会有些复杂,因为您必须考虑读者的许多特点和要求,然而,即使是像这样丑陋的东西也可以做到这一点:

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

:)

一句忠告:与其通过简单的宏“改进”基本的公共Lisp语法,不如在编辑器的帮助下自动插入您认为冗长的代码。defclass的语法是初学者编写宏的一个特殊受害者。@hans23非常好的建议。另一个可能的选择是使用
defstruct
而不是
defclass
。更重要的是,除了定义initargs和accessor之外,它还定义了一种可读的打印方法。建议:不要通过简单的宏来“改进”基本的公共Lisp语法,而是使用编辑器的帮助来自动插入您认为冗长的代码。defclass的语法是初学者编写宏的一个特殊受害者。@hans23非常好的建议。另一个可能的选择是使用
defstruct
而不是
defclass
。更重要的是,除了定义initargs和访问器之外,它还定义了一个可读的打印方法。