【问题标题】:Macro that generates macro form wrapped in let bound variables生成包含在 let 绑定变量中的宏形式的宏
【发布时间】:2014-02-18 19:56:33
【问题描述】:

我一直在尝试编写一个宏来生成同名的编译器宏等。这是我坚持的最小代码:

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

【问题讨论】:

    标签: macros lisp common-lisp quote


    【解决方案1】:

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

    (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 已经足够聪明,可以用普通引号替换反引号,因为里面没有任何东西需要不加引号。生成被拼接的表单的mapcar 无法生成其中包含逗号的代码,因为它没有指定这些逗号是如何实现的,并且根据2.4.7 Comma,“如果在除内部之外使用逗号,则逗号无效反引号表达式的主体”。我认为这意味着您最好的选择是:

    (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 (compiler-macro-function 'foobar) '(foobar 10 11) ())" 可以是除了macroexpand-1之外验证输出的有用工具。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-16
    • 2016-03-07
    • 2013-10-02
    • 2011-06-12
    • 1970-01-01
    • 2022-12-18
    相关资源
    最近更新 更多