Macros 生成封装在let绑定变量中的宏窗体的宏

Macros 生成封装在let绑定变量中的宏窗体的宏,macros,lisp,common-lisp,quote,Macros,Lisp,Common Lisp,Quote,我一直在尝试编写一个宏来生成一个同名的编译器宏。这是我遇到的最简单的代码: (defmacro definline (name lambda-list &body body) `(define-compiler-macro ,name ,lambda-list `(let ,,(mapcar (lambda (v) ``(,',v ,,v)) lambda-list) ,,@body))) 我想要的是这样的东西: (definline foobar (a

我一直在尝试编写一个宏来生成一个同名的编译器宏。这是我遇到的最简单的代码:

 (defmacro definline (name lambda-list &body body)
  `(define-compiler-macro ,name ,lambda-list
     `(let ,,(mapcar (lambda (v) ``(,',v ,,v)) lambda-list)
        ,,@body)))
我想要的是这样的东西:

(definline foobar (a b) (print "foobar") (+ a b))
;; Expands to
(define-compiler-macro foobar (a b)
  `(let ((a ,a) (b ,b))
     (print "foobar") (+ a b)))
但是我不知道如何生成let绑定
((a,a)(b,b))
。我一直无法理解的问题是如何生成编译器宏表单,以便lambda绑定的内容在扩展中不带引号。我知道如何手动执行此操作,但我不确定如何对任意lambda列表执行此操作

编辑:

我又做了些手脚,终于想出了这个主意。这很有效。但是,这太可怕了

(defmacro definline (name lambda-list &body body)
  (read-from-string
   (format nil "(define-compiler-macro ~S (~{~S~^ ~})
                    `(let (~{(~S ,~S)~})
                       ~{~S~}))"
           name lambda-list (loop for l in lambda-list nconc (list l l)) body)))

如果修改了一些与I/O相关的变量,则使用
格式的方法根本不起作用。使用
格式
生成符号名通常非常脆弱且不可移植。但这是一个很难解决的问题,用列表结构来解决它可能比单用反引号更容易。例如,在这种情况下,我们可以:

(defmacro definline (name variables &body body)
  (list 'define-compiler-macro name variables
        `(list* 'let (list ,@(mapcar (lambda (variable)
                                       `(list (quote ,variable) ,variable))
                              variables))
                ',body)))
其工作原理如下:

CL-USER> (pprint (macroexpand-1 '(definline foobar (a b) 
                         (print "foobar")
                         (+ a b))))

(DEFINE-COMPILER-MACRO FOOBAR
    (A B)
  (LIST* 'LET (LIST (LIST 'A A) (LIST 'B B)) '((PRINT "foobar") (+ A B))))
除非我误读了什么,否则结果应该与:

(define-compiler-macro foobar (a b)
  `(let ((a ,a) (b ,b))
     (print "foobar")
     (+ a b)))
我不认为只使用反报价就一定可以生成后一种形式。问题是,由于规范没有准确定义如何实现反向报价,所以它不像下面这样简单

(define-compiler-macro foobar (a b)
  (backquote (let ((a (unquote a))
                   (b (unquote b)))
               (print "foobar")
               (+ a b)))
如果您的实现确实以这种方式实现了它,那么您可以编写一个扩展来生成这种类型的输出。除非您从实现中得到这样的保证,否则我认为没有办法获得需要注入到更高层的“逗号变量”。很难清楚地说明这一点,但您可以看看这一尝试:

(defmacro definline (name variables &body body)
  `(define-compiler-macro ,name ,variables
     `(let ,',(mapcar (lambda (variable)
                        `(,variable (unquote ,variable)))
                      variables)
        ,@',body)))
这将产生如下结果:

(DEFINE-COMPILER-MACRO FOOBAR
    (A B)
  '(LET ((A (UNQUOTE A)) (B (UNQUOTE B)))
     (PRINT "foobar")
     (+ A B)))
请注意,SBCL已经足够聪明,可以用一个普通的引号替换backquote,因为里面没有需要取消引号的内容。生成拼接表单的
mapcar
无法生成包含逗号的代码,因为它没有指定如何实现这些逗号,并且根据“如果在反引号表达式主体之外使用逗号,则逗号无效”。我认为这意味着你最好的选择是:

(defmacro definline (name variables &body body)
  `(define-compiler-macro ,name ,variables
     `(let ,(mapcar 'list 
                    ',variables
                    (list ,@variables))
        ,@',body)))
在不同的实施方式下,其扩展将有所不同,但在SBCL中:

(DEFINE-COMPILER-MACRO FOOBAR (A B)
  `(LET (SB-IMPL::BACKQ-COMMA (MAPCAR 'LIST '(A B) (LIST A B)))
     (PRINT "foobar")
     (+ A B)))
在CCL中,您可以获得:

(DEFINE-COMPILER-MACRO FOOBAR (A B)
  (LIST* 'LET
         (LIST* (MAPCAR 'LIST '(A B) (LIST A B))
                '((PRINT "foobar") (+ A B)))))
在CLISP中:

(DEFINE-COMPILER-MACRO FOOBAR (A B)
 (CONS 'LET
  (CONS (MAPCAR 'LIST '(A B) (LIST A B)) '((PRINT "foobar") (+ A B)))))

调用生成的编译器宏函数,例如“(funcall(编译器宏函数‘foobar’)(foobar 10 11)()”),除了macroexpand-1之外,在这里也是验证输出的有用工具。