Macro Issues: Eval of a macro body works, but the macro doesn't

时间:2017-06-09 12:46:48

标签: macros lisp common-lisp

Consider the following code snippet:

[1]> (defvar *clist* '((2 1 21) ( 3 2 32) (4 3 43)))
*CLIST*
[2]> (eval `(case '1 ,@(mapcar #'rest *clist*)))
21
[3]> (defmacro tester (index clist)
      `(case ,index ,@(mapcar #'rest clist))) 
TESTER
[4]> (tester '1 *clist*)
*** - MAPCAR: A proper list must not end with *CLIST*
The following restarts are available:
ABORT          :R1      Abort main loop
Break 1 [5]> 

The code contains the error-message generated.
As one can clearly see, that eval of the code that is used as the body of the macro tester , gives a result. But the same code (by replacing *clist* and '1, by clist and index variables.) doesn't work when it's used as a body of the macro.

2 个答案:

答案 0 :(得分:6)

测试backquote时,只需打印它:

> `(case '1 ,@(mapcar #'rest *clist*))
(CASE '1 (1 21) (2 32) (3 43))

在测试宏时,你会评估它们(在REPL或者全部 更是如此,使用eval 明确地)。

使用macroexpand扩展宏 并检查代码。

如,

> (macroexpand-1 '(tester '1 *clist*))
*** - MAPCAR: A proper list must not end with *CLIST*

这告诉您tester代替symbol *CLIST* 它的值为mapcar

你需要考虑你要做的事情 "compile-time" vs "execution time"

  • 您是否在编译时知道index
  • 您是否在编译时知道clist

在您的情况下,没有理由使用case

(defmacro tester (index clist) `(third (find ,index ,clist :key #'second)))
(macroexpand-1 '(tester 1 *clist*))
==> (THIRD (FIND 1 *CLIST* :KEY #'SECOND)) ; T
(tester 1 *clist*)
==> 21

因为你在编译时不知道clist(只有 存储它的变量名称,有 使用case没有胜利 - 它必须在编译时知道所有子句。

答案 1 :(得分:1)

让我们拿你的宏,删除扩展并打印它的参数:

CL-USER 4 > (defmacro tester (index clist)
              (print (list :macro-tester :index index :clist clist))
              nil)
TESTER

现在我们用你的例子来称呼它:

CL-USER 5 > (tester 1 *clist*)

(:MACRO-TESTER :INDEX 1 :CLIST *CLIST*) 
NIL

因此index1clist为符号*clist*

现在让我们尝试使用函数mapcar和符号rest来调用*clist*

CL-USER 9 > (mapcar #'rest '*clist*)

Error: *CLIST* (of type SYMBOL) is not of type LIST.
  1 (abort) Return to level 0.
  2 Return to top loop level 0.

这是你看到的错误。符号*clist*不是有效列表。