如果修改了某些与 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)))))