#+title: (reduce) #+pubdate: <2020-11-04> #+OPTIONS: ^:nil One habit I had to initially overcome coming to Clojure was a tendency to loop recur all the time instead of reducing. Here's a snippet from #clojure where I came to ask about the habit that I saved from that time: ------ *neeasade* hello all -- newbie clojure user here, and I've noticed I tend to solve almost all my problems with loop+recur forms. just wondering if that's like, maybe a sign I'm still doing stuff procedurally. *justin_smith* neeasade: yeah - for example any time you do (recur (rest x)) is a sign you are doing it the hard way. clojure has many functions to abstract variants of that pattern (traversing a collection in order) without needing to explicitly do a procedural loop. then it's a question of learning what the various collection functions are useful for (filter, map, mapcat, reduce, for etc.). *neeasade* I guess the problem I have is I feel like usually I have a collection of 'moves' or 'ops' that I want to apply to 'state' and have state change along the way. *justin_smith* that sounds like reduce or reductions to me. *neeasade* maybe reduce is what I need to get comfy with then (I'm comf with map/reduce/filter). *justin_smith* it caries a state (usually called an "accumulator" but it can be anything) between each step. *neeasade* cool -- thanks, I've use reducing/reducers before, I think I just fell out of habit because it doesn't feel "natural" to me yet. *justin_smith*: #+begin_src clojure (reduce (fn [acc i] (let [res (+ acc i)] (if (> res 100) (reduced res) res))) 0 (range)) ;; => 105 #+end_src *justin_smith* neeasade: also reduce has a built in debugger, you can s/reduce/reductions/. #+begin_src clojure (reductions (fn [acc i] (let [res (+ acc i)] (if (> res 100) (reduced res) res))) 0 (range)) ;; => (0 0 1 3 6 ...) #+end_src *neeasade* thanks justin_smith -- appreciate it! ----- Now, let's compare what this difference looks like with easy-ranked [[http://www.4clojure.com/problem/40][4clojure problem]]: Write a function which separates the items of a sequence by an arbitrary value. #+begin_src clojure ;; replace the '__' (= (__ 0 [1 2 3]) [1 0 2 0 3]) (= (apply str (__ ", " ["one" "two" "three"])) "one, two, three") (= (__ :z [:a :b :c :d]) [:a :z :b :z :c :z :d]) #+end_src Before (bad habits): #+begin_src clojure (fn [sep coll & state] (if (= 1 (count coll)) (conj state (first coll)) (recur sep (rest coll) (conj (or state []) (first coll) sep)))) #+end_src We're doing a lot of manual management of state here, to carry it through the solution. After: #+begin_src clojure (fn [sep coll] (butlast (reduce (fn [state next] (conj state next sep)) [] coll))) #+end_src Just gotta remember to embrace reducin', and the implicit carrying of state it gives us. #+begin_center [[https://clojuredocs.org/clojure.core/reduce][(reduce)]] #+end_center