lisp宏扩展为部分eval

时间:2011-07-01 12:37:50

标签: lisp expand ccl

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

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

2 个答案:

答案 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))) 示例现在是否有效。这应该会让你走上正轨。

最后,建议:在函数执行时避免使用宏。它将使实施者和用户的生活更加轻松。