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.

Reply via email to