Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Append to clojure vector from within loop

Tags:

clojure

I have:

(defn keep?

   (def sum [])
     (loop [i 0]
        (when (< i 10)
           (conj sum 10)
           (recur (inc i))))
     sum
   )

This just gives me and empty vector even though I am conj-ing 10 onto sum. Is this because it is not in-scope within the Loop? How would I achieve the same outcome. (btw, this example is deliberately simplified)

Thanks

like image 395
Zuriar Avatar asked Jan 08 '23 03:01

Zuriar


2 Answers

conj does not modify its argument. In fact, without resorting to evil reflection tricks, nothing will modify a vector, it's an immutable data structure. This is one of the fundamental principles of Clojure.

In functional programming, instead of modifying data, we replace it with another immutable value. So you need to use the return value of conj, or it is effectively a noop.

(defn keep?
  []
  (loop [i 0 sum []]
    (if (< i 10)
      (recur (inc i) (conj sum 10))
      sum)))

Also, the second arg to defn must always be a vector.

like image 111
noisesmith Avatar answered Jan 15 '23 16:01

noisesmith


conj is not destructive, it does not alter that collection, returns a new collection with the designated state (reference).

To achieve the desired result, you may:

  • Define sum in a loop-form, like i is defined, instead of using def
  • recur (inc i) (conj sum 10) to rebind sum to a new one on every iteration, so that state is built up to what you expect
  • Once the condition in when is not met, just return sum from your loop and it will bubble up to become the return value of this function. Uh hang on, when does not have an "else-branch", a possible alternative is if

Like so:

(defn keep? []
     (loop [i   0
            sum []]
           (if (< i 10)
               (recur (inc i)
                      (conj sum 10))
               sum)))
like image 42
D-side Avatar answered Jan 15 '23 15:01

D-side