Macros 为什么使用“的宏;让我们;扩展与不扩展的扩展不同';T

Macros 为什么使用“的宏;让我们;扩展与不扩展的扩展不同';T,macros,lisp,common-lisp,let,Macros,Lisp,Common Lisp,Let,我是Lisp新手,我正在阅读Doug Hoyte的《让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))

我是Lisp新手,我正在阅读Doug Hoyte的《让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)))))
当我进行
(宏扩展(niffy2 10“正”“零”“负”)
时,我得到了我所期望的:
(LET((X 10))(COND((PLUSP X)“正”)((zeropx)“零”(T“负”))


但是当我执行
(宏扩展(niffy 10“正”“零”“负”)
时,我只得到求值形式
“正”
。这让我很困惑,因为在
niffy
中,
cond
被反引用,所以我认为这意味着它不会被评估。在不进行宏扩展的情况下,对
niffy
niffy2
进行求值,这两个函数的工作方式与我预期的完全相同,分别为正值、零值和负值返回“正”、“零”和“负”。

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

(macroexpand
 '(cond ((plusp 10) "positive")
        ((zerop 10) "negative")
        (t "zero")))
在SBCL中,我得到以下信息:

(IF (PLUSP 10)
    (PROGN "positive")
    (COND ((ZEROP 10) "negative") (T "zero")))
T
"positive" ;
T
在CLISP中,我得到以下信息:

(IF (PLUSP 10)
    (PROGN "positive")
    (COND ((ZEROP 10) "negative") (T "zero")))
T
"positive" ;
T
虽然我可以理解CLISP的行为,但这似乎有点不必要,因为在宏扩展之后可以更容易地处理优化


(请注意,您通常应该更喜欢带有
let
的版本,因为它不会多次计算其参数。)

您的代码有错误:

(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
编译器抱怨:

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)(niffy 10“正”“零”“负”),我只得到计算形式“正”

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

请注意,
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

啊,好的,我在用clisp。那么clisp刚刚编译了
cond
还是什么?另外,现在我看到如果我做了
macroexpand-1niffy
我得到了我期望的行为。看起来CLISP中
cond
的宏只是在做一些优化。编译将导致不同的输出,我认为CLISP有一个带字节码输出的编译器。(顺便说一句,CLISP已经失效)确实
t
不是一个函数,但这不是一个错误,因为它没有被用作函数(至少在添加缺少的括号时是这样)。@DietrichEpp:Lisp系统不会知道括号放错了。但作为一个人,我们知道代码中只有一个错误:缺少括号。Lisp系统是否足够复杂,能够解决这个问题是另一回事。@DietrichEpp:我不确定有多少人可以通过查看代码轻松找到它。如果稍后Lisp系统投诉。它抱怨函数调用——这就是我提到它的原因。然后,我们必须回过头来找出T被用作函数的原因。是的,但你已经找出了实际的错误在哪里。值得一提的是,Lisp系统可能会抱怨
t
不是一个函数,值得解释的是,这是由于缺少括号造成的,值得教人们如何从诊断消息中找出错误,但仍然只有一个错误:缺少括号。