Say I have a predicate that takes two items and returns true of false. I want to insert an item in between every consecutive pair in a sequence that returns true for the predicate. I've come up with a couple of solutions but I was wondering what would be idiomatic way to do it functionally in Clojure?
My first draft would be something like
(defn insert-between [pred inter coll]
(lazy-seq
(when-let [s (seq coll)]
(cons (first s)
(mapcat (fn [[left right]]
(if (pred left right)
[inter right]
[right]))
(partition 2 1 s))))))
user> (insert-between < :less [1 6 7 4 3])
(1 :less 6 :less 7 4 3)
Seems to work, but I'm special-casing the first element in an ugly way and I think you could get around that. The solution could definitely be improved, anyway.
This is my try at it:
(defn interpose-predicated [pred in coll]
(if (next coll)
(->> coll
(partition 2 1)
(mapcat (comp next #(if (apply pred %) (interpose in %) %)))
(cons (first coll)))
coll))
It's the usual SO [clojure] race to come up with the most concise solution. :-) I usually don't win, but I learn a lot in the process. Anyway, here is my solution:
(defn interpose-p [[a b & _ :as s] d p]
(when-not (empty? s)
(if (and (not (nil? b)) (p a b))
(cons a (cons d (interpose-p (rest s) d p)))
(cons a (interpose-p (rest s) d p)))))
(interpose-p [1 2 3 2 1 2 3] "," <)
(1 "," 2 "," 3 2 1 "," 2 "," 3)
Update: Even though the discussion is over, here is an updated solution taking into account everyone's comments. This time is should be fairly industrial strength assuming my understanding of lazy-seq is correct. It is templated off of the lazy-seq discussion here.
(defn interpose-p
[pred coll sep]
(let [f (fn [c]
(when-let [[a b & _ :as s] (seq c)]
(if (and b (pred a b))
(list* a sep (interpose-p pred (rest s) sep))
(list* a (interpose-p pred (rest s) sep)))))]
(lazy-seq (f coll))))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With