我有以下代码让我感到困惑,我希望有些人可以告诉我区别以及如何解决这个问题。
(defmacro tm(a)
`(concat ,(symbol-name a)))
(defun tf(a)
(list (quote concat) (symbol-name a)))
我认为它们应该是相同的效果,但实际上它们似乎不是。
我尝试跟随电话:
CL-USER> (tf 'foo)
(CONCAT "FOO")
CL-USER> (tm 'foo)
value 'FOO is not of the expected type SYMBOL.
[Condition of type TYPE-ERROR]
那么,问题是什么?
我想要的是:
(tm 'foo) ==> (CONCAT "FOO")
答案 0 :(得分:4)
第一个问题是读者将'foo
扩展为(quote foo)
,这不是符号,而是列表。宏试图扩展(tm (quote foo))
。列表(quote foo)
作为参数a
传递给宏扩展函数,该函数尝试获取其symbol-name
。列表不是symbol-name
的有效参数。因此,您的宏扩展失败。
第二个问题是虽然(tm foo)
(注意:没有引用)展开到(concat "FOO")
,但这个表单将由REPL执行,所以这是也与您的tf
功能不同。当然,这并不奇怪,因为宏的功能与功能不同。
答案 1 :(得分:2)
首先,请注意
`(concat ,(symbol-name a))
和
(list (quote concat) (symbol-name a))
做同样的事情。它们是等效的代码片段(反引用语法不限于宏体!):两者都构造一个列表,其第一个元素是符号CONCAT
,其第二个元素是变量{{1}的符号名称}指的是。
显然,只有当A
引用一个符号时才有意义,正如Svante所指出的那样,在宏调用示例中并非如此。
你可以,当然,从列表A
中提取符号,但这会阻止你像这样调用宏:
(QUOTE FOO)
这引发了一个问题,即为什么你想要强制宏的用户明确地引用它需要为文字常量的符号。
其次,宏的工作方式是:它们将代码片段(例如(let ((x 'foo))
(tm x))
)作为参数并生成一段新代码,在宏扩展时,(或多或少)替换宏调用源代码。在生成的代码中重用宏参数通常是有用的,方法是将它们放在稍后要评估的位置,例如在
(QUOTE FOO)
考虑一下这段代码的作用以及我上面的(defmacro tm2 (a)
`(print (symbol-name ,a)))
示例现在是否有效。这应该会让你走上正轨。
最后,建议:在函数执行时避免使用宏。它将使实施者和用户的生活更加轻松。