Solving Clojure problems on 4ever-clojure (44/151)
I've started to learn Clojure by solving the problems over at 4ever-clojure. There are currently 151 problems on that site. I just solved number 44 and thought I'd take some time to reflect.
conj function confused me at first because I didn't understand its
purpose, which is to ask a collection to make a new collection with an
element added to it in a place that makes sense to the collection.
When the collection is a vector it makes sense to add the element to
the end of it, but for sequences it's more efficient to add it to the
Problem 30 is about removing consecutive duplicates from a sequence.
'(9 3 3 2 1 1 1 3 1) should become
'(9 3 2 1 3 1). Here's
how I solved it.
(def p30a (fn [x] (reverse (loop [source x acc '()] (if (empty? source) acc (if (= (first source) (second source)) (recur (drop 1 source) acc) (recur (drop 1 source) (conj acc (first source)))))))))
Needlessly complicated, you say? I agree. "I'll come back to this later and try to write a shorter version", was what I was going to say but of course I couldn't let it go so here's my second attempt.
(def p30b (fn [A] (reverse (reduce #(if (= (first %1) %2) %1 (conj %1 %2)) '() A))))
I got rid of one
loop and two
recur:s at the cost of introducing one
reduce, which is an improvement, I think.
Problem 31: pack consecutive duplicates into sub-lists. For example,
'(1 1 2 1 1 1 3 3) should become
'((1 1) (2) (1 1 1) (3 3)). Not too
happy with how I solved this one either.
(def p31 (fn [x] (reverse (loop [lst x, acc '()] (if (empty? lst) acc (let [dacc (take-while (partial = (first lst)) lst)] (recur (drop (count dacc) lst) (conj acc dacc))))))))
I was really into
recur at this point. It looks terrible,
there must be a much simpler solution but I'll leave it for now.
Problem 39: Write a function which takes two sequences and returns the first item from each, then the second item from each, then the third, etc. I managed to produce a good solution to this one, I think.
(def p39 (fn [A B] (mapcat vector A B)))
Looks great, if I do say so myself. I got there by realizing that a
map function can operate on multiple collections at once, and then I
just happened to find
mapcat which conveniently concat:s the
Problem 41: drop every Nth item from a sequence. I don't love what I came up with.
(def p41 (fn [A n] (map #(nth A %) (filter #(< 0 (mod (inc %) n)) (range (count A))))))
At least I'm just using plain functions like
Problem 42: factorial function. Another one I'm pretty proud of.
(def p42 (fn [x] (reduce * (map inc (range x)))))
I have no idea how that could be improved but we'll see.
Problem 43: reverse the interleave process into x number of
subsequences. For example,
'(1 2 3 4 5 6) 2 should become
'((1 3 5) (2
4 6)). This is the first "medium" difficulty problem I solved, all the
others were "easy" or "elementary".
(def p43 (fn [A n] (let [steps (filter #(= 0 (mod % n)) (range (count A)))] (for [i (range n)] (map #(nth A (+ i %)) steps)))))
I'm not loving the
for call, feels like there should be a cleaner
Problem 44: rotate a sequence in either direction. For example,
2 3 4 5) should become
'(3 4 5 1 2), and
-2 '(1 2 3 4 5) should become
'(4 5 1 2 3). My first solution p44a felt a little clunky so I wrote
p44b that uses a different approach.
(def p44a (fn [d A] (let [nA (count A) s (map #(mod (+ % d) nA) (range nA))] (map #(nth A %) s)))) (def p44b (fn [d A] (let [x (mod d (count A))] (concat (drop x A) (take x A)))))