为什么使用 "let" 的宏与不使用 "let" 的宏展开方式不同?
Why does a macro that uses "let" expand differently from one that doesn't?
我是 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
被反引号括起来,所以我认为这意味着它不会被评估。在没有宏扩展的情况下评估 niffy
和 niffy2
都完全按照我的预期工作,返回 "positive"、"zero" 和 "negative" 用于正数、零和负数值分别。
这是因为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
的版本,因为它不会多次计算其参数。)
您的代码有错误:
(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))))
But when I do (macroexpand '(niffy 10 "positive" "zero" "negative")) I just get the evaluated form "positive".
因为你要求宏展开,所以无法计算。它必须是该实现中宏扩展器的效果。
请注意,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
我是 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
被反引号括起来,所以我认为这意味着它不会被评估。在没有宏扩展的情况下评估 niffy
和 niffy2
都完全按照我的预期工作,返回 "positive"、"zero" 和 "negative" 用于正数、零和负数值分别。
这是因为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
的版本,因为它不会多次计算其参数。)
您的代码有错误:
(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))))
But when I do (macroexpand '(niffy 10 "positive" "zero" "negative")) I just get the evaluated form "positive".
因为你要求宏展开,所以无法计算。它必须是该实现中宏扩展器的效果。
请注意,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