On Thu, Feb 16, 2012 at 14:06 -0600, Michael Gardner wrote: > On Feb 16, 2012, at 11:08 AM, Wolodja Wentland wrote: > > > Thanks for the explanation. What would be a good way to ensure that the > > subseqeuence are lazy too? > > I can't think of a good way to do this with higher-order functions, so > you'll probably have to write a recursive function that "manually" iterates > over the collection, looking ahead as necessary to figure out when to split. > To make the result lazy, just wrap your function's body in a call to > lazy-seq.
Just for the sake of completeness and in the case somebody stumbles over this
thread in the future. I solved the problem for now and have a lazy
implementation of split-at-subsequence that behaves like split does for
strings:
--- snip ---
user> (split-at-subsequence [0] (range 10))
((1 2 3 4 5 6 7 8 9))
user> (split-at-subsequence [1 2] (range 10))
((0) (3 4 5 6 7 8 9))
user> (split-at-subsequence [1 2] [1 2 0 1 2 1 1 2 3)
; Evaluation aborted.
user> (split-at-subsequence [1 2] [1 2 0 1 2 1 1 2 3])
((0) (1) (3))
user> (split-at-subsequence [1 2] [1 2 0 1 2 1 1 2 2])
((0) (1) (2))
user> (split-at-subsequence [1 2] [1 2 1 2 1 2])
()
user> (first (second (split-at-subsequence [1 2] (range))))
3
(defn prefix?
"Return true if sub is a prefix of s"
[sub s]
(loop [sub (seq sub), s (seq s)]
(or (nil? sub)
(and s
(= (first sub) (first s))
(recur (next sub) (next s))))))
(defn replace-subsequence
"Replaces all instances of sub with replacement in s"
[sub replacement s]
{:pre [(seq sub)]}
(when-let [s (seq s)]
(lazy-seq
(if (prefix? sub s)
(cons replacement
(replace-subsequence sub replacement
(drop (count sub) s)))
(cons (first s)
(replace-subsequence sub replacement
(next s)))))))
(defn replace-first-subsequence
"Replaces the first instance of sub with replacement in s"
[sub replacement s]
{:pre [(seq sub)]}
(when-let [s (seq s)]
(lazy-seq
(if (prefix? sub s)
(cons replacement (drop (count sub) s))
(cons (first s)
(replace-first-subsequence sub replacement
(next s)))))))
(defn- splitter
[delimiter s]
(lazy-seq
(when-let [s (seq s)]
(let [data? #(not (identical? delimiter %))]
(if (not (identical? delimiter (first s)))
(cons (take-while data? s)
(splitter delimiter (drop-while data? s)))
(splitter delimiter (rest s)))))))
(defn split-at-subsequence
"Splits s at instances of sub"
[sub s]
(let [delimiter (Object.)]
(splitter delimiter (replace-subsequence sub delimiter s)))
--- snip ---
I'd also like to thank all the people in #clojure who discussed this problem
with me and in particular TimMc for his valuable remarks and help.
Due to me being quite new to Clojure I would appreciate any feedback on the
code above and wonder if something like this should be part of the standard
library.
--
Wolodja <[email protected]>
4096R/CAF14EFC
081C B7CD FF04 2BA9 94EA 36B2 8B7F 7D30 CAF1 4EFC
signature.asc
Description: Digital signature
