我们假设:
(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是一个符号而不是列表?我该如何防止这种情况?
由于
答案 0 :(得分:4)
您可以执行以下操作:
(defmacro testing (&optional var)
`(if (consp ,var)
'(this is a list)
'(this is)))
因此将在运行时(不是编译时)评估var
。 var
仅在宏的扩展中出现一次,但如果它出现不止一次,则必须使用gensym。
编辑:如果您不想输入'(this is)
两次,请执行以下操作:
(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
),它只会编译一次。
答案 1 :(得分:0)
这里的问题是表达式
,@(when (consp var) `('a 'list))))
当您只有文字(未评估)参数值时,在编译时进行评估。在您的情况下:2
,(list 1 2)
和bla
。
我知道的唯一解决方法是使用eval
。这个特定的例子可以改变如下:
(defmacro testing (&optional var)
`(eval (append '(list 'this 'is)
(when (consp ',var)
'('a 'list))))
但是,我认为,你会同意,它真的很难看。如果你想使用词法变量,它将无法工作。通常,有一些方法可以重新解决问题,因此不需要这种变态。