Hi all, I am trying to implement a function that splits any given sequence on a subsequence. I would like it to behave like this:
user> (split-at-subsequence [5 6] (range 10))
((0 1 2 3 4) (7 8 9))
user> (split-at-subsequence [5 7] (range 10))
((0 1 2 3 4 5 6 7 8 9))
user> (first (second (split-at-subsequence [1 2] (range))))
3
The code I have so far inserts sentinels for the given subsequence and then
returns chunks until a sentinel is found.
(defn starts-with-subseq?
"Returns true if the first sequence starts with the second one"
[sub-seq coll]
(let [pairs (map vector coll sub-seq)
matching-pairs (take-while #(= (first %) (second %)) pairs)]
(= (count matching-pairs) (count sub-seq))))
(defn- replace-subseq-with-sentinel
[sub-seq sentinel coll]
(lazy-seq
(when-let [s (seq coll)]
(if (starts-with-subseq? sub-seq s)
(cons sentinel
(replace-subseq-with-sentinel sub-seq sentinel (drop (count
sub-seq) s)))
(cons (first s)
(replace-subseq-with-sentinel sub-seq sentinel (rest s)))))))
(defn- split-at-sentinel
[sentinel coll]
(take-nth 2 (partition-by #(= sentinel %) coll)))
(defn split-at-subsequence
[sub-seq coll]
(let [sentinel (Object.)]
(->> (replace-subseq-with-sentinel sub-seq sentinel coll)
(split-at-sentinel sentinel)))
This does exactly what it should do, except for the fact that
user> (first (second (split-at-subsequence [1 2] (range))))
never returns as it does not seem to be "lazy enough". What surprises me is
that
user> (def sentinel (Object.))
#'user/sentinel
user> (take 5 (replace-subseq-with-sentinel [1 2] sentinel (range)))
(0 #<Object java.lang.Object@272a335c> 3 4 5)
works just fine, so the problem must be in my implementation of
split-at-sentinel. I would be grateful for any pointers that might shed light
on this behaviour. I am using clojure 1.3 if that is of any importance.
Thanks for your time and may you have a nice day!
--
Wolodja <[email protected]>
4096R/CAF14EFC
081C B7CD FF04 2BA9 94EA 36B2 8B7F 7D30 CAF1 4EFC
signature.asc
Description: Digital signature
