1

我现在有以下代码让我感到困惑,我希望有人能告诉我区别以及如何解决这个问题。

(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")
4

2 回答 2

4

第一个问题是'foo被读者扩展为(quote foo),它不是一个符号,而是一个列表。宏试图展开(tm (quote foo))。该列表(quote foo)作为参数传递a给宏扩展函数,该函数试图获取其symbol-name. 列表不是 的有效参数symbol-name。因此,您的宏扩展失败。

第二个问题是,虽然(tm foo)(注意:没有引号)确实扩展(concat "FOO"),但此表单将由 REPL 执行,因此这也与您的tf函数不同。当然,这并不奇怪,因为宏做的事情与函数不同。

于 2011-07-01T13:25:59.867 回答
2

首先,请注意

`(concat ,(symbol-name a))

(list (quote concat) (symbol-name a))

做同样的事情。它们是等效的代码段(反引号语法不限于宏体!):两者都构造一个列表,其第一个元素是符号CONCAT,第二个元素是变量A所指的符号名称。

显然,这只有在引用一个符号时才有意义A,正如 Svante 所指出的,在宏调用示例中并非如此。

当然,您可以(QUOTE FOO)从 list 中提取符号,但这会阻止您像这样调用宏:

(let ((x 'foo))
  (tm x))

这就提出了一个问题,即为什么您要强制宏的用户在无论如何都需要是文字常量的地方显式引用符号。

其次,宏的工作方式是这样的:它们将一段代码(例如(QUOTE FOO))作为参数并生成一段新代码,在宏展开时,(或多或少)替换源代码中的宏调用。在生成的代码中重用宏参数通常很有用,方法是将它们放在稍后将要评估的位置,例如

(defmacro tm2 (a)
  `(print (symbol-name ,a)))

想想这段代码做了什么,我let上面的例子现在是否有效。这应该让你走上正轨。

最后,一条建议:当函数可以使用时避免使用宏。它将使实施者和用户的生活变得更加轻松。

于 2011-07-01T15:09:19.390 回答