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 <babi...@gmail.com>

4096R/CAF14EFC
081C B7CD FF04 2BA9 94EA  36B2 8B7F 7D30 CAF1 4EFC

Attachment: signature.asc
Description: Digital signature

Reply via email to