Search code examples
clojurereduce

How is this code with reduce function evaluating in Clojure?


Following is the Clojure code:

(reduce (fn [r x] (if (nil? x) r (conj r x)))  
        []  
        [:mouse nil :duck nil :lory nil])

In REPL, it evaluates to [:mouse :duck :lory].

My question is, how is the code evaluating?

According to me, r is [] and x is [:mouse nil :duck nil :lory nil]. nil? x is false as so it evaluates to (conj r x). But x is a vector, not an element so how it will add an element to the empty vector r in conj? I don't know but I am wrong somewhere in my approach. The output is the animals' name vector without nil values. Can anyone please explain me the execution of code. Thanks.


Solution

  • Your problem appears to be understanding how reduce works. I'd like to refer you to the source code, but it simply maps onto a Java implementation, so I have to fake it.

    The kind of reduce you are doing - supplying the initial value - might have been coded as follows:

    (defn reduce [f init coll]
      (loop [acc init, tail coll]
        (if (seq tail)
          (recur (f acc (first tail)) (rest tail))
          acc)))
    

    As you can see, it works through the sequence coll, applying the function f to acc and the first value in the sequence, to generate a new acc. When there is no more sequence, it returns acc.

    How does this apply to your example? The reducing function ...

    (fn [r x] (if (nil? x) r (conj r x)))
    

    ... ignores nil xs but conjs anything else onto the end of the accumulating vector r. Your code is more or less equivalent to ...

    (remove nil? [:mouse nil :duck nil :lory nil])
    => (:mouse :duck :lory)
    

    ... except you get a lazy sequence instead of a vector.