Reputation: 2339
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
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
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
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