2

我正在阅读Paul Graham 的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 参数。是这样吗?

4

2 回答 2

4

关键是,如果找到匹配项,宏版本会懒惰地评估参数(它扩展为 OR)停止。这不能通过函数来​​实现,因为 funcall 总是首先评估所有参数。

于 2012-05-13T10:31:54.023 回答
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))
于 2012-05-13T11:22:18.020 回答