Yar
Yar

Reputation: 7457

Re-implementing Partition in clojure

I am trying to find a method to implement Partition (with [] padding) in clojure. I think it's doable using loop and recur and mapping it into the list:

 (defn collect-h [v n]
   (loop [i n
          res []
          lst v
          ]
     (if (= 0 i)
       res
       (recur (dec i) (cons (first lst) res) (next lst))
       )
     )
   )

So the problem is that implementation only works on the first series of answer "(collect-h [1 2 3 4 5 6 7 8 9 10] 3) will give ((1 2 3))". So I need to map it to the whole collection and remove the first n number in every loop, but that doesn't look really efficient. I wonder if there is a better way to solve it.

Edit:

so it should work like this:

(collect-h [1 2 3 4 5 6 7 8 9 10] 3)                   ;; ((1 2 3) (4 5 6) (7 8 9) (10))

which is same to

(partition 3 3 [] [1 2 3 4 5 6 7 8 9 10])

Upvotes: 1

Views: 259

Answers (3)

Timothy Pratley
Timothy Pratley

Reputation: 10662

How about this?

(defn partition-ghetto [n xs]
  (if (seq xs)
    (cons (take n xs) (partition-ghetto n (drop n xs)))
    ()))

(partition-ghetto 3 (range 10))
=> ((0 1 2) (3 4 5) (6 7 8) (9))

Definitely not as good as the core version, but might provide some ideas?

Note that this recursive definition is not tail recursive, so will blow the stack for large sequences, nor is it lazy like most Clojure sequence functions. The advantage of laziness on sequences is that you are neither stack nor heap bound when operating on a stream. See alternative answers below that provide solutions to these concerns.

Upvotes: 2

Mike
Mike

Reputation: 3416

Building off @Timothy-Pratley and the Clojure source code, you could also use lazy-seq:

(defn partition-ghetto [n xs]
  (lazy-seq (when-let [s (seq xs)]
              (cons (take n s) (partition-ghetto n (drop n s))))))

Upvotes: 3

leetwinski
leetwinski

Reputation: 17859

@Timothy-Pratley answer is nice, but it is not tail recursive, meaning that it would cause stack overflow in case of large collection. Here is non stack consuming variant:

(defn my-partition [n items]
  (loop [res [] items items]
    (if (empty? items)
      res
      (recur (conj res (take n items))
             (drop n items)))))

user> (my-partition 3 (range 10))
[(0 1 2) (3 4 5) (6 7 8) (9)]

Upvotes: 5

Related Questions