Hey all -- I recently found myself needing to check for pairwise property consistency across a sequence of elements, similar in spirit to inequality checking (in this case, date comparisons using clj-time *t/before?*). What I wanted was to do something like:
(if (t/before? date-a date-b date-c date-d) ; elided ) ...which is symmetric to the idiomatic usage of the inequality functions in clojure core (*=*, *>* and friends). Unfortunately, *t/before?* does not support variadic invocation, and after reading the source behind each of those functions, I discovered that they implement their own variadic overloads ad hoc, and there doesn't appear to be (at least I haven't yet discovered) a way to generalize pairwise property assertion over an arbitrarily large sequence of things. I would previously have solved this like: (reduce (fn [_ [x y]] (if (t/before? x y) true (reduced false))) true (partition 2 1 [(t/local-date 2015 1 1) (t/local-date 2015 2 1) (t/local-date 2015 3 1)])) ;-> true ...or even... (every? (fn [[x y]] (t/before? x y)) (partition 2 1 [(t/local-date 2015 1 1) (t/local-date 2015 2 1) (t/local-date 2015 3 1)])) ...which is sort of cumbersome and occludes my intent. I ended up writing a function to address this problem generally, and thought it might be useful to others (and possibly worth including in clojure). Here it is: (defn pairwise? "Returns true if every sequential pair satisfies pred; false otherwise." {:inline (fn [pred x y] `(~pred ~x ~y)) :inline-arities #{3}} ([_ _] true) ([pred x y] (pred x y)) ([pred x y & more] (if (pairwise? pred x y) (if (next more) (recur pred y (first more) (next more)) (pairwise? pred y (first more))) false))) ...and the test cases: (deftest pairwise? (testing "Idiomatic cases" (is (pairwise? > 3 2 1 0)) (is (pairwise? t/after? (t/local-date 2015 3 1) (t/local-date 2015 2 1) (t/local-date 2015 1 1))) (is (pairwise? #(= %1 (* -1 %2)) -1 1 -1 1)) (is (not (pairwise? = 1 1 1 2 1)))) (testing "Short circuits on false" (is (not (apply pairwise? = (concat [2 2] (repeat 1))))))) It accepts as its first argument any binary predicate, and applies it to a splat until the predicate returns false. The keen eye will notice it looks very similar to the variadic overload of *>*, which is what I used for reference. This means you can now do things like: (pairwise? t/before? (t/local-date 2015 1 1) (t/local-date 2015 2 1) (t/local-date 2015 3 1)) ;-> true ...even though *t/after?* only supports a dyadic form. This also works nicely with apply to run against seqs: (apply pairwise? t/before? [(t/local-date 2015 1 1) (t/local-date 2015 2 1) (t/local-date 2015 3 1)]) ;-> true Or, for properties that aren't necessarily correlated to ordering: (pairwise? #(= %1 (* -1 %2)) -1 1 -1 1) ;-> true Which of course works for the inequality operators (although they obviously support this natively): (pairwise? > 3 2 1 0) ;-> true As i said, I was unable to find anything in clojure core similar to *pairwise?*, although admittedly I've only been using clojure in anger now for around 4 months, so if I'm overlooking an in-built capability, please set me straight. Thoughts/suggestions/amendments/corrections welcome and appreciated! Cheers, -J -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.