I find myself in a situation when I need to combine several predicate into one. Is there a standard way of doing this, something similar to compliment
?
Suppose there are several simple predicates (e.g. is-fruit-p
, is-red-p
, grows-on-trees-p
etc.) and a list of objects from which a subset must be filtered out using more than one predicate. What's the better way of achieving this than the following:
(remove-if #'is-fruit-p
(remove-if #'is-red-p
(remove-if #'grows-on-trees-p list-of-objects)))
Are you sure that a special syntax would really help? Consider the following
(lambda (x)
(and (is-fruit-p x)
(or (grows-on-tree-p x)
(is-red-p x))))
and now the slightly more general
(lambda (x)
(and (is-fruit-p x)
(or (grows-on-tree-p x)
(eq (color x) 'red))))
or
(lambda (x)
(and (is-fruit-p x)
(or (grows-on-tree-p x)
(eq (color x) desired-color)))) ; desired-color captured lexical
Even if you build up a special syntax for predicates do you think the added language complexity is worth the rigidity you will get? For example are you going to define a predicate #'weights-exactly-five-ounces-p
? What about #'weights-up-to-and-including-six-and-half-ounces-p
?
If you start needing a parametric predicate and use lambda forms for that then using a combiner you're going to write more code than not using it because the (lambda (x) ...)
wrapper will be needed for each parametric term. More importantly that code will be also harder to read (in addition to having to learn a special new macro for predicate combination).
IMO it may make sense to write and/or combiners if you're passed in predicates and you need to pass predicates to someone else... but not for writing the code you used in the example; for that I'd write
(remove-if (lambda (x) (or (is-fruit-p x)
(is-red-p x)
(grows-on-trees-p x)))
list-of-objects)
Less to write, less to read, nothing extra to learn, trivial to parametrize.
Suppose for example that you want a list of fruits with the same color as the one you have (in mine
) and with same weight or possibly heavier...
(remove-if-not (lambda (x) (and (is-fruit-p x)
(eq (color x) (color mine))
(>= (weight x) (weight mine))))
objects)
Higher order functions like disjoin
and conjoin
are available in the quicklisp installable alexandria library.
CL-USER> (ql:quickload "alexandria")
...
CL-USER> (remove-if (alexandria:disjoin #'zerop #'oddp #'minusp)
'(0 -1 1 -2 2))
=> (2)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With