Macros 为什么使用“的宏;让我们;扩展与不扩展的扩展不同';T
我是Lisp新手,我正在阅读Doug Hoyte的《让Lambda过去》,他在第3章介绍了Paul Graham的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))
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
不是一个函数,值得解释的是,这是由于缺少括号造成的,值得教人们如何从诊断消息中找出错误,但仍然只有一个错误:缺少括号。