Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a function similar to "andmap" in clojure?

Tags:

clojure

I want to apply a series of tests on my list and make sure that all the tests are passed. Is there a function similar to "andmap" in Clojure?

like image 830
unj2 Avatar asked Jul 30 '09 22:07

unj2


4 Answers

You could use every?:

user=> (every? string? '("hi" 1))
false

Here's the documentation on every?.

like image 85
Pinochle Avatar answered Oct 28 '22 20:10

Pinochle


Clojure 1.3 will add every-pred (and the related some-fn for the "or" version).

clojure.core/every-pred ([p] [p1 p2] [p1 p2 p3] [p1 p2 p3 & ps])

Takes a set of predicates and returns a function f that returns true if all of its composing predicates return a logical true value against all of its arguments, else it returns false. Note that f is short-circuiting in that it will stop execution on the first argument that triggers a logical false result against the original predicates.

A naive implementation might be:

(defn every-pred [& preds] (fn [& args] (every? #(every? % args) preds)))

but the actual implementation will have better performance.

like image 33
miner49r Avatar answered Oct 28 '22 19:10

miner49r


I wrote andmap as a macro which takes predicates as its arguments and builds a function that "wraps an and around the predicates", i.e.,

(andmap integer? odd?) 
==>
(fn [x] (and (integer? x)
             (odd? x)))

(it doesn't expand to exactly this, but it expands to something equivalent to this)

This has the advantage that it shortcuircuts on the predicates so you can write

(every? (andmap integer? odd?) [1 3 "a string"])

without getting a runtime exception as you would get with Arthurs answer.

Here is the definition of andmap:

(defmacro andmap 
  ([]       `(fn [& x#] true))
  ([p & ps] `(fn [& x#] (and (apply ~p x#)
                             (apply (andmap ~@ps) x#)))))

It is also possible to define andmap as an function which also short-circuits on it's predicates due to lazyness:

(defn andmap [& ps]
  (fn [& x]
    (every? true? (map (fn [p] (apply p x)) ps))))

The predicates to andmap can take an arbitrary number of arguments, so it is possible to write

(map (andmap #(and (integer? %1)
                   (integer? %2))
             #(and (odd? %1)
                   (even? %2))
             <)
     [1 3 9]
     [2 6 "string"])

which evaluates to (true true false).

like image 45
Jonas Avatar answered Oct 28 '22 21:10

Jonas


every? will ask "Does this one function return true for each member of the seq", which is close to what I think you are asking for. An improvement on every? would take a list of functions and ask "Are all these predicates true for every member of this seq".

Here is a first attempt:

(defn andmap? [data tests]
  (every? true? (for [d data, f tests]
                  (f d))))

user> (andmap? '(2 4 8) [even? pos?])
true
user> (andmap? '(2 4 8) [even? odd?])
false
like image 37
Arthur Ulfeldt Avatar answered Oct 28 '22 20:10

Arthur Ulfeldt