taylor_venable wrote:I ran into a problem right away though when trying to write all which takes a predicate and a list, returning true when all the elements in the list satisfy the predicate.
First, note that such function already exists:
every.
taylor_venable wrote:My first thought was that this is just a fold of the
and operation into the result of mapping the predicate over the list. That means basically applying
and to all the list elements, so in my ignorance I tried to do:
Code: Select all
(apply 'and (mapcar #'evenp '(2 4 6 8 9))) => nil
This is not a fold. Apply calls a function with arguments given in a list. A fold is done by reduce function, and in this case, since and is a macro, would look:
Code: Select all
(reduce #'(lambda (lv rv) (and lv rv)) (mapcar #'evenp '(2 4 5 8 9)))
Note that this is not short-circuiting, as mapcar will obviously evaluate the predicate on all arguments. It can be done trivially by iterating on the list:
Code: Select all
(dolist (e '(2 4 5 8 9) t)
(unless (evenp e) (return nil)))
If you really want to use a macro as a function you can always wrap it in lambda. It is different from eval in that the function is constructed at compile-time (or maybe even read-time?), not run-time. But this is rarely needed in practice, because for short-circuiting operators functional versions already exist (every, some, notevery, notany).
taylor_venable wrote:Which leads me to my question: is it considered acceptable style to use eval to do something like this? I typically shun the use of eval in other languages, because it's usually just a cheap way of getting something done which could be handled in more elegant ways, but with the data-is-code orientation of Lisp, is this a common tactic to employ?
In my (not very extensive, mind you) experience using eval is very rare, and it's use can be almost always replaced with something better.
taylor_venable wrote:I'd also be interested to see any alternate implementations of and;
I'm just making this up, so this is not even remotely efficient and may even not work, but:
Code: Select all
(defmacro my-and (&body body)
(let ((step-fun (gensym))
(thunk-list (gensym))
(thunks (gensym)))
`(let ((,thunk-list (list ,@(loop
for b in body
collect `(lambda ()
,b)))))
(labels ((,step-fun (,thunks)
(cond ((null ,thunks)
t)
((not (funcall (car ,thunks)))
nil)
(t (,step-fun (cdr ,thunks))))))
(,step-fun ,thunk-list)))))
taylor_venable wrote: I thought it might be cool to have a macro that expanded into [using the example above] (and t t t t nil) but I can't seem to make it happen. My best guess was (...)
Expanding to and is not a good idea for the same reason apply isn't when used on arbitrary sequences, there is an argument number limit. In any case, evaluating the predicate at compile time would require the arguments to be known at compile time, and then why would you expand to (and t t t nil) at all, if you could just compute nil right there?
I hope that helped in some way.