为什么使用" let"的宏?扩展与不同的扩展不同?

时间:2015-03-07 04:03:12

标签: macros lisp common-lisp let

我是Lisp的新手,我正在阅读Doug Hoyte的Let Over Lambda,他在第3章介绍了Paul Graham的nif宏。我正在玩这个并制作这两个宏:

(defmacro niffy (expr pos zero neg)
  `(cond ((plusp ,expr) ,pos)
         ((zerop ,expr) ,zero)
         (t ,neg)))

(defmacro niffy2 (expr pos zero neg)
  `(let ((x ,expr))
     (cond ((plusp x) ,pos)
           ((zerop x) ,zero
            (t ,neg)))))

当我(macroexpand '(niffy2 10 "positive" "zero" "negative"))时,我得到了我期望的结果:(LET ((X 10)) (COND ((PLUSP X) "positive") ((ZEROP X) "zero" (T "negative"))))

但是当我(macroexpand '(niffy 10 "positive" "zero" "negative"))时,我只得到评估表格"positive"。这使我感到困惑,因为在niffy中,cond被反引号,所以我认为这意味着它不会被评估。在没有宏扩展的情况下评估niffyniffy2都完全符合我的预期,分别为正值,零值和负值返回“正”,“零”和“负”。

2 个答案:

答案 0 :(得分:3)

这是因为cond是一个宏。尝试运行以下内容:

(macroexpand
 '(cond ((plusp 10) "positive")
        ((zerop 10) "negative")
        (t "zero")))

在SBCL,我明白了:

(IF (PLUSP 10)
    (PROGN "positive")
    (COND ((ZEROP 10) "negative") (T "zero")))
T

在CLISP中,我明白了:

"positive" ;
T

虽然我可以理解CLISP的行为,但似乎有点不必要,因为在宏扩展之后可以更容易地处理优化。

(请注意,您通常应该选择let版本,因为它不会多次评估其参数。)

答案 1 :(得分:1)

您的代码有错误

(defmacro niffy2 (expr pos zero neg)
  `(let ((x ,expr))
     (cond ((plusp x) ,pos)
           ((zerop x) ,zero              <- missing parenthesis
            (t ,neg)))))                 <- T is not a function

测试:

(defun test (x)
  (niffy2 x "positive" "zero" "negative"))

编译器抱怨:

The following function is undefined:
T which is referenced by TEST

应该是:

(defmacro niffy2 (expr pos zero neg)
  `(let ((x ,expr))
     (cond ((plusp x) ,pos)
           ((zerop x) ,zero)
           (t ,neg))))
  

但是当我这样做时(macroexpand&#39;(niffy 10&#34;积极&#34;&#34;零&#34;&#34;消极&#34;))我只是得到评估表格&# 34;正&#34;

由于您要求进行宏扩展,因此无法对其进行评估。它必须是该实现中宏扩展器的效果。

请注意MACROEXPAND是一个迭代过程。表格将扩大。结果表单可以是另一个宏表单。它也将得到扩展。然后再次。再次。直到结果表单不是宏表格。请注意,这不会遍历子表单。它严格按照顶层形式进行迭代宏扩展。

使用MACROEXPAND-1

因此,如果您只想查看宏的效果,请致电MACROEXPAND-1。此功能只会扩展一次。

CL-USER 23 > (macroexpand-1 '(niffy 10 "positive" "zero" "negative"))
(COND ((PLUSP 10) "positive")
      ((ZEROP 10) "zero")
      (T "negative"))

CL-USER 24 > (macroexpand-1 '(COND ((PLUSP 10) "positive")
                                   ((ZEROP 10) "zero")
                                   (T "negative")))
(IF (PLUSP 10)
  (PROGN "positive")
  (IF (ZEROP 10)
     (PROGN "zero")
     (PROGN "negative")))
T