4

I want to take a list and generate subsequences, in this manner:

(subs '(1 2 3))
; should evaluate to ((1) (1 2) (1 2 3) (2) (2 3) (3))

and yes, order matters. I might be missing something obvious, but I'm stuck. Bonus points for pretty solutions!

1
  • 1
    Think recursively. If you could generate all subsequences of (2 3), could you then get all subsequences of (1 2 3)? Commented Jan 26, 2012 at 10:27

6 Answers 6

4

Another take, a little shorter.

(defn subs4 [coll]
   (mapcat #(reverse (take (count %) (iterate butlast %)))
      (take (count coll) (iterate rest coll))))
Sign up to request clarification or add additional context in comments.

Comments

4
(defn subseqs
  [coll]
  (lazy-seq
    (when-let [s (seq coll)]
      (let [n (inc (count coll))]
        (concat (map take (range 1 n) (repeat s))
                (subseqs (rest s)))))))

nil-safe variation on ponzao's answer.

Edit: One more using reductions.

(defn subseqs
  [coll]
  (lazy-seq
    (when-let [s (seq coll)]
      (let [fst (first s)
            rst (rest s)]
        (concat (reductions conj [fst] rst) (subseqs rst))))))

Note, that this version is fully lazy! Well, until you traversed the first full subsequence. This is as lazy as you can get.

The following version is also fully lazy, but constructs the reductions only once at startup.

(defn subseqs
  [coll]
  (let [step (fn step [subcolls]
               (lazy-seq
                 (when-let [s (next subcolls)]
                   (concat s (step (map next s))))))]
    (step (reductions conj [] coll))))

1 Comment

Good catch, I will fix this in my answer.
3

My version with recursion:

(defn my-subs
  ([lst] (my-subs lst (count lst)))
  ([lst, len]
     (when (> len 0)
       (concat
        (for [i (range 1 (+ 1 len))]
          (take i lst))
        (my-subs (rest lst) (- len 1))))))

(my-subs '(1 2 3))                        
; => ((1) (1 2) (1 2 3) (2) (2 3) (3))

Comments

3
(defn subseqs
  [[x & xs :as coll]]
  (when (seq coll)
    (lazy-cat
     (for [n (range 1 (inc (count coll)))]
       (take n coll))
     (subseqs xs))))

Comments

3
(defn subs3 [coll]
  (apply concat
    []
    (map
      #(reverse (for [x (iterate butlast %) :while (not-empty x)] x))
      (for [x (iterate rest coll) :while (not-empty x)] x))))

Comments

2

No recursion needed, just plain ole nested for loops

  (defn subs
    [seq]
    (let [c (count seq)]
      (remove empty?
        (for [x (range c) y (range (- (inc c) x))]
          (take y (drop x seq))))))

and another one

(defn subs
  [seq]
 (letfn [(take-len [f s] (take (count s) (iterate f s)))]
   (mapcat #(reverse (take-len butlast %)) (take-len next seq))))

Just noticed this is about the same as e-i-s's version. Had been trying to roll my own version of butlast (because of the symmetry with "iterate next"), but none were making things more succinct than the nested for loops. Until I found butlast on clojuredocs.

Anyways, try out the 4clojure problems. Not being able to get to the answers really forces you to find an answer, any answer that works, and if you're doing/have done that you'll find a more elegant one. And if you got no idea, there's usually answers of other users that will enlighten or inspire after you solved it.

1 Comment

I've found butlast on the Clojure cheatseet which is really cool.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.