Reputation: 5954
Say there is the need to check if an argument passes one truth test of a given predicate collection.
codewise:
(fn [x]
(or (pred1 x) (pred2 x) (pred3 x) (pred4 x)))
due to the implementation of or
, this short circuits after the first truthy value. As intended.
How can this be rewritten by using a collection of predicates:
[pred1 pred2 pred3 pred4]
A funky way would be:
(fn [x preds]
(some? ;; nil->false
(some true? (map #(% x) preds))))
It also turns out that this one does not short circuit. Might be due to Clojure's chunking of lazy sequences.
Can we do this better?
Upvotes: 1
Views: 158
Reputation: 78639
Alternatively,
(defn somep? [x [p & ps :as preds]]
(if-not (empty? preds)
(or (p x) (somep? x ps))))
or
(defn somep? [x [p & ps :as preds]]
(if-not (empty? preds)
(let [res (p x)]
(if-not res
(recur x ps)
res))))
Upvotes: 0
Reputation: 512
When I need short circuit, I use reduce with reduced.
(defn any-valid? [w & pred-fn-coll]
(reduce (fn [v pf]
(if (pf w)
(reduced true)
v)) false pred-fn-coll))
(any-valid? 1 even? odd?)
;=> true
(any-valid? 1 even? even?)
;=> false
Upvotes: 0
Reputation: 13483
I think it's map
that's doing the chunking in your solution.
Try
(defn any-true? [preds]
(fn [x]
(loop [preds preds]
(and (seq preds)
(or ((first preds) x)
(recur (rest preds)))))))
((any-true? [odd? even?]) 3) ;true
((any-true? []) 3) ;nil
((any-true? [even?]) 3) ;nil
((any-true? [odd? #(/ % 0)]) 3) ;true
The last example shows that the evaluation is lazy.
Upvotes: 0
Reputation: 17849
clojure has a some-fn
function for that:
user> ((some-fn true? false? nil?) true)
true
user> ((some-fn false? nil?) true)
false
or for your case:
user> (defn any-pred? [x preds]
((apply some-fn preds) x))
another classic way is to do it recursively:
user> (defn any-pred? [x preds]
(when-let [[pred & preds] (seq preds)]
(or (pred x) (any-pred? x preds))))
user> (any-pred? true [false?])
nil
user> (any-pred? true [true?])
true
user> (any-pred? true [false? true?])
true
user> (any-pred? true [false? nil?])
nil
user> (any-pred? true [false? nil? true?])
true
Upvotes: 4