我正在阅读Paul Graham's ANSI Common Lisp。在关于宏的章节中,他展示了以下示例:
(defmacro in (obj &rest choices)
(let ((insym (gensym)))
`(let ((,insym ,obj))
(or ,@(mapcar #'(lambda (c) `(eql ,insym ,c))
choices)))))
(如果第一个参数等于任何其他参数,则返回true)
他认为它不能写成函数。这个功能不会具有相同的功能吗?
(defun in (obj &rest choices)
(reduce (lambda (x y)
(or x (eql y obj)))
choices
:initial-value nil))
我看到的区别是宏只会评估参数,直到找到一个eql参数。是吗?
答案 0 :(得分:4)
关键是,如果找到匹配项,宏版本会懒惰地评估参数(它会扩展为OR)。使用函数无法实现这一点,因为funcall将始终首先评估所有参数。
答案 1 :(得分:3)
> (macroexpand '(in 42
(long-computation-1)
(long-computation-2)
(long-computation-3)))
(LET ((#:G799 42))
(OR (EQL #:G799 (LONG-COMPUTATION-1))
(EQL #:G799 (LONG-COMPUTATION-2))
(EQL #:G799 (LONG-COMPUTATION-3))))
要获得相同的效果,您需要写:
(defun in (obj &rest choices)
(reduce (lambda (x y)
(or x (eql (funcall y) obj)))
choices
:initial-value nil))
并以这种方式使用它:
(in 42
(function long-computation-1)
(function long-computation-2)
(function long-computation-3))