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)