Last time I talked about mapeach, a macro which is a simple wrapper around mapcar. After using mapeach a couple times, I found that I wanted each version of many other other functions, remove, find, and count to name a few. One option I had was to write a macro for every single one of these functions. If I were to have done this, I would have wound up with remove-each, find-each, and so on. Instead I took door number two, creating a general macro which I call hofeach. Hofeach, is just like mapeach, except it takes an extra argument for the HOF (higher order function), that you want to use. Below is one possible implementation of hofeach.
(defmacro hofeach (hof var list &body body) `(funcall ,hof (lambda (,var) ,@body) ,list))
Here is what code that uses hofeach as a fill in for mapeach looks like:
(hofeach #'mapcar x '(1 2 3) (* x x)) => (1 4 9)
Now we get to specify which HOF we want to use! If we want to keep all of the numbers in a list that are even, here is how we could do that:1 2
(hofeach #'remove-if-not x '(1.2 5 7 2 3.5 6 9) (and (integerp x) (evenp x))) => (2 6)
So now that I have hofeach, I generally will use it instead of passing a complex lambda expression to a HOF. Most of the time I use hofeach with remove-if-not, but I have also used it with count-if as well. It gives code a nice down and to the right look, which I find pretty easy to read. You get to read the forms in the order that they appear. If you were to use a lambda expression instead, it becomes much more difficult to read since you have to jump around in order to read the code.
- Remove-if-not is a pretty bad name. Removing all of the elements that do not satisfy some property can be thought of as keeping all of the ones that do. So instead of being called 'remove-if-not', it should be called 'keep-if'.
- We need to use integerp since evenp signals an error when called with a non-integer value.