Lazily partition a sequence into differently-sized chunks in Clojure - clojure

How to lazily partition a sequence into differently-sized chunks in Clojure? Sort of like (partition n xs), but for a sequence of ns. For example:
(chunker [3 4 5] (range 12))
=> ((0 1 2) (3 4 5 6) (7 8 9 10 11))

I needed this to chunk some inputs and didn't want to use Instaparse. Here is a lazy solution that supports cycled chunk sizes:
(defn chunker
"Like (partition N input) but for a sequence of Ns."
[[chunk & chunks] coll]
(lazy-seq
(when-let [s (seq coll)]
(cons (take chunk s)
(when chunks (chunker chunks (drop chunk s)))))))
Usage
(chunker [3 4 5] (range 20))
=> ((0 1 2) (3 4 5 6) (7 8 9 10 11)) ;; note not input not fully consumed.
(chunker (cycle [3 4 5]) (range 20))
=> ((0 1 2) (3 4 5 6) (7 8 9 10 11) (12 13 14) (15 16 17 18) (19))

Related

Iterate over a list with a given step in clojure

I'm trying to iterate over a list with a given step in clojure.
In python I would have done the following :
xs = list(range(10))
xs[::2]
# out: [0, 2, 4, 6, 8]
xs[1::2]
# out: [1, 3, 5, 7, 9]
I can't figure out a clojure solution that feels idiomatic.
Here is the best I can think of:
(defn iterate-step-2 [xs]
(map first (take-while some? (iterate nnext xs))))
(iterate-step-2 (range 10))
; out: (0 2 4 6 8)
(iterate-step-2 (rest (range 10)))
; out: (1 3 5 7 9)
But it's not as generic (step is not configurable) and as flexible as the python solution. Plus it seems overly complicated.
Is there a better way to do this ?
You can use take-nth for this:
user=> (take-nth 2 (range 10))
(0 2 4 6 8)
user=> (take-nth 2 (rest (range 10)))
(1 3 5 7 9)
;; equivalent to Python's your_seq[1:7:2] would be:
(->> your-seq (drop 1) (take 7) (take-nth 2))
;; equivalent to Python's your_seq[::2] would be:
(->> your-seq (take-nth 2))
;; equivalent to Python's your_seq[2:4:-3] would be:
(->> your-seq (take 4) (drop 2) (reverse) (take-nth 3))
;; equivalent to Python's your_seq[2:-4:-1]:
(->> your-seq (take (+ 1 (- (length your-seq) 4))) (drop 2) (reverse))
Another option is to generate the desired index values, and then use those for the lookup:
(let [N 10
data (vec (range N)) ; `vec` is optional but faster than using (lazy) list if large N
idxs (range 1 10 2)
result (mapv #(nth data %) idxs)]
)
with result:
N => 10
data => [0 1 2 3 4 5 6 7 8 9]
idxs => (1 3 5 7 9)
result => [1 3 5 7 9]
or something less simple:
(let [N 9999
data (vec (range N)) ; `vec` is optional but faster than using (lazy) list if large N
idxs (mapv #(Math/pow 2 %) (range 11))
result (mapv #(nth data %) idxs)]
with result:
idxs => [1.0 2.0 4.0 8.0 16.0 32.0 64.0 128.0 256.0 512.0 1024.0]
result => [1 2 4 8 16 32 64 128 256 512 1024]
Interesting! Apparently nth will accept a floating-point index value as long as it has a zero fraction. :)

How to check if current value is greater than the next value in Clojure?

I am looking for a way to check if a current value in a collection is greater than the next value, and if so, add that pair of items to a collection eg:
[9 2 3 7 11 8 3 7 1] => [9 2 11 8 8 3 7 1] ; Checking each item against the next
I initially thought I could do something like:
(filter (fn [[x y]] (> x y)) [9 2 3 7 11 8 3 7 1])
But something like this seemed to work only with associative types. So then I tried something like this:
(defn get-next [col index] ; Returns next item from a collection
(get col (inc (.indexOf col index))))
(filter (fn [[x]] (> x (get-next [9 2 3 7 11 8 3 7 1] x))) [9 2 3 7 11 8 3 7 1])
But still I got the same error. Any help would be appreciated
Use partition function to make pair of current and next item in a collection.
user=> (partition 2 1 [9 2 3 7 11 8 3 7 1])
((9 2) (2 3) (3 7) (7 11) (11 8) (8 3) (3 7) (7 1))
Now you have pair of current and next item in the collection. you can compare the items in each pair and concat the result with mapcat.
user=> (->> [9 2 3 7 11 8 3 7 1]
#_=> (partition 2 1)
#_=> (mapcat (fn [[a b]] (if (> a b) [a b]))))
(9 2 11 8 8 3 7 1)
Another way is to use reduce:
(defn pairs [data]
((reduce (fn [res item]
(if (and (:prev res) (< item (:prev res)))
(assoc res
:prev item
:res (conj (:res res) (:prev res) item))
(assoc res :prev item)))
{:prev nil :res []} data) :res))
(pairs [9 2 3 7 11 8 3 7 1])
;; [9 2 11 8 8 3 7 1]

List of a..b in clojure

In Clojure, I can have a sequence a..b with (range a b). But this is a lazy sequence as I understand. Can I just generate a list and/or vector of numbers a..b?
Note: I am new to Clojure.
do you mean something like
user> (vec (range 2 7))
[2 3 4 5 6]
user> (apply list (range 2 7))
(2 3 4 5 6)
user> (into [] (range 2 7))
[2 3 4 5 6]
user> (into '() (range 2 7))
(6 5 4 3 2) ; <-- note the order
user> (into #{} (range 2 7))
#{2 3 4 5 6}

How does this function which reverses the interleave process into x number of subsequences

I completed exercise 43 on 4clojure the other day and checked some of the other solutions. One in particular has confused me.
The challenge asks you to write a function which satisfies all of these:
(= (__ [1 2 3 4 5 6] 2) '((1 3 5) (2 4 6)))
(= (__ (range 9) 3) '((0 3 6) (1 4 7) (2 5 8)))
(= (__ (range 10) 5) '((0 5) (1 6) (2 7) (3 8) (4 9)))
My solution was this:
(fn [l n]
(map #(map second %) (vals (group-by #(mod (first %) n)
(map vector (iterate inc 0) l)))))
User himself had this solution:
#(apply map list (partition %2 %1))
and I couldn't work out how it worked.
Let's work through the first problem:
(= (__ [1 2 3 4 5 6] 2) '((1 3 5) (2 4 6)))
well the (#(partition %2 %1) [1 2 3 4 5 6] 2) would give us ((1 2) (3 4) (5 6)) now how does apply map list on that produce (1 3 5) (2 4 6)
apply is using the ((1 2) (3 4) (5 6)) as a variable length list of additional arguments. Then iteration of the map is applying the list function to all three of these additional lists.
As a result it expands as follows:
(apply map list '((1 2) (3 4) (5 6)))
=> (map list '(1 2) '(3 4) '(5 6))
=> (list 1 3 5) and (list 2 4 6)

Sliding window over seq

In Clojure, what would be the nicest way to have a sliding window over a (finite, not too large) seq? Should I just use drop and take and keep track of the current index or is there a nicer way I'm missing?
I think that partition with step 1 does it:
user=> (partition 3 1 [3 1 4 1 5 9])
((3 1 4) (1 4 1) (4 1 5) (1 5 9))
If you want to operate on the windows, it can also be convenient to do this with map:
user=> (def a [3 1 4 1 5 9])
user=> (map (partial apply +) (partition 3 1 a))
(8 6 10 15)
user=> (map + a (next a) (nnext a))
(8 6 10 15)
I didn't know partition could do this so I implemented it this way
(defn sliding-window [seq length]
(loop [result ()
remaining seq]
(let [chunk (take length remaining)]
(if (< (count chunk) length)
(reverse result)
(recur (cons chunk result) (rest remaining))))))