galdre
galdre

Reputation: 2339

Clojure: Test a sequence for a running property

Suppose you want to test a Clojure sequence for a property that is defined only in terms of contiguous elements. For example, monotonicity.

(defn monotonic?
    [sequence]
    (every? #(<= (first %) (second %))
            (partition 2 1 sequence)))

But I actually want to do this for a number of different properties. I could copy this for each, or I could abstract:

(defn sequence-has-property?
    [f sequ]
    (every? #(apply f %)
            (partition 2 1 sequ)))

If I want to abstract out the number of previous terms on which a test is dependent (for example, so that #(= %3 (+ % %2)) could test for a generalized Fibonacci pattern), I could write:

(defn sequence-has-property? [f sequ n] (every? #(apply f %) (partition n 1 sequ)))

Question: Is there a better (faster/more idiomatic) way?

Upvotes: 3

Views: 390

Answers (3)

hariszaman
hariszaman

Reputation: 8432

simpler solution

(defn monotonic? [a-seq]
   (or (apply <= a-seq) (apply >= a-seq)
  )
)
(monotonic? [1 2 3])     => true
(monotonic? [0 1 10 11]) => true
(monotonic? [1 2 1 0])   => false

Upvotes: 0

NielsK
NielsK

Reputation: 6956

Taken from linked question of OP:

Just make the predicate function itself take variadic arguments, and have it do the partitioning / recurring. Your monotonic? for instance already exists in core, and is called <=

(<= 1 2 4 5)
=> true
(<= 1 2 1 5)
=> false

Here's the source for the 1, 2 and variadic arg versions:

(source <=)
(defn <=
  "Returns non-nil if nums are in monotonically non-decreasing order,
  otherwise false."
  {:inline (fn [x y] `(. clojure.lang.Numbers (lte ~x ~y)))
   :inline-arities #{2}
   :added "1.0"}
  ([x] true)
  ([x y] (. clojure.lang.Numbers (lte x y)))
  ([x y & more]
   (if (<= x y)
     (if (next more)
       (recur y (first more) (next more))
       (<= y (first more)))
     false)))

You can make a fib? work the same way, have it take variadic arguments and recur over triples:

(defn fib?
  [a b & [c & r]]
  (if (= c (+ a b))
    (if r
      (recur b c r)
      true)
    false))

(fib? 0 1 1)
=> true

(fib? 2 3 5 8 13)
=> true

Upvotes: 2

amalloy
amalloy

Reputation: 92077

No, there is no way to do it without knowing N. All your definitions of sequence-has-property look good to me, with no major improvements available. I'd use (partial apply f) rather than #(apply f %) in this context, though. I don't have a clear reason why, since in other contexts I prefer the lambda to using partial - I just think it looks better this time.

Upvotes: 1

Related Questions