如果某些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)))
我不认为使用backquotes来生成后一种形式是不可能的。问题是,由于该规范并没有定义反引号究竟是如何实现的,它不是像
(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(编译器宏函数'foobar)'(foobar 10 11)())”除了宏扩展1之外可以成为验证输出的有用工具。 –
感谢您的优秀解释! – asm