Macros Common Lisp:如何使用条件拼接在宏中构建列表?
让我们假设:Macros Common Lisp:如何使用条件拼接在宏中构建列表?,macros,common-lisp,splice,Macros,Common Lisp,Splice,让我们假设: (defmacro testing (&optional var) `(list 'this 'is ,@(when (consp var) `('a 'list)))) 调用时: >(testing 2) (THIS IS) >(testing (list 1 2)) (THIS IS A LIST) 这就是我想要的。但现在,当我传递一个列表参数时: >(defparameter bla (list 1 2 3)) BLA
(defmacro testing (&optional var)
`(list 'this 'is
,@(when (consp var) `('a 'list))))
调用时:
>(testing 2)
(THIS IS)
>(testing (list 1 2))
(THIS IS A LIST)
这就是我想要的。但现在,当我传递一个列表参数时:
>(defparameter bla (list 1 2 3))
BLA
>(testing bla)
(THIS IS)
我想这是因为宏会选中(consp bla)
,其中bla是一个符号,而不是列表?我如何防止这种情况
谢谢这里的问题是
,@(when (consp var) `('a 'list))))
当您只有参数的文字(未计算)值时,在编译时计算。在您的情况下:2
,(列表12)
,以及bla
据我所知,唯一的解决办法是使用eval
。此特定示例可以更改如下:
(defmacro testing (&optional var)
`(eval (append '(list 'this 'is)
(when (consp ',var)
'('a 'list))))
但是,我想,你会同意的,那真的很难看。如果你想使用词法变量,它是行不通的。通常,有一些方法可以重新表述问题,这样就不需要这样的歪曲了。您可以这样做:
(defmacro testing (&optional var)
`(if (consp ,var)
'(this is a list)
'(this is)))
因此,var
将在运行时(而不是编译时)进行计算var
在宏的展开中只出现一次,但如果出现多次,则必须使用gensym
编辑:如果不想键入两次”(这是)
,请执行以下操作:
(defmacro testing (&optional var)
`(append '(this is) (when (consp ,var) '(a list))))
不要使用eval
,它很慢,而且完全没有必要。通过将var
替换到宏扩展中,自然会在运行时对其进行评估。如果使用eval,您将执行以下操作:
(eval (append '(list 'this 'is) (when (consp 'bla) '('a 'list))))
每次执行时,它都会建立一个表示代码的列表,并在运行之前对其进行编译。(希望这不是一个循环!)如果你只使用一个生成简单代码的宏(没有
eval
),它只会编译一次。在这种(非常简化的)情况下,这会起作用,但我试图避免在条件拼接中必须键入两次'。@mck,我刚刚为您发布了符合该要求的代码。谢谢,这是一个优雅的解决方案。如果将if替换为when,并删除nil(when在false时返回nil),则可以缩短时间。关于“在运行时自然评估”的另一个问题。当我在某些代码上尝试此操作时,它会很好地返回列表,但无法对其求值?@mck,你说它无法对其求值是什么意思<代码>(测试(列表12));=>(这是一个列表)您可以在这里看到,(列表1 2)
已经过评估,结果是consp
,因此它返回(这是一个列表)
。你能发布你正在尝试的代码吗?谢谢你的跟进。这是一个后续问题,如果不使用eval,宏似乎不会运行。我做错什么了吗?谢谢,这很管用(尽管我认为“事后检查”需要取消吗?)。我希望能有更优雅的东西,但也许我真的应该试着重新表述一下……关于报价——这取决于你想如何传递数据。在你的情况下,这不是真的需要-这是真的。但通常你会在宏中看到这一点,宏需要“轻量级”列表,比如(13)