Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boolean functors in lisp

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)))
like image 715
zzandy Avatar asked Mar 18 '13 11:03

zzandy


2 Answers

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)
like image 148
6502 Avatar answered Oct 17 '22 10:10

6502


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)
like image 4
huaiyuan Avatar answered Oct 17 '22 10:10

huaiyuan