Reputation: 117
I'm reading Paul Graham's ANSI Common Lisp. In the chapter about macros he shows the following example:
(defmacro in (obj &rest choices)
(let ((insym (gensym)))
`(let ((,insym ,obj))
(or ,@(mapcar #'(lambda (c) `(eql ,insym ,c))
choices)))))
(Returns true if the first argument is equal to any of the other arguments)
He holds that it can't be written as a function. Wouldn't this function have the same functionality?
(defun in (obj &rest choices)
(reduce (lambda (x y)
(or x (eql y obj)))
choices
:initial-value nil))
The difference I see is that the macro will only evaluate arguments till it finds an eql argument. Is that it?
Upvotes: 2
Views: 158
Reputation: 139241
> (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))))
To get the same effect you would need to write:
(defun in (obj &rest choices)
(reduce (lambda (x y)
(or x (eql (funcall y) obj)))
choices
:initial-value nil))
and use it this way:
(in 42
(function long-computation-1)
(function long-computation-2)
(function long-computation-3))
Upvotes: 3
Reputation: 31053
The point is, that the macro version evaluates the arguments lazily (it expands into an OR) stopping if a match is found. This cannot be achieved with a function, since a funcall will always evaluate all arguments first.
Upvotes: 4