jasonaburton
jasonaburton

Reputation: 3113

conj not updating vector inside of loop

I'm trying to teach myself clojure. This is just supposed to be a simple function that takes a value and adds each of its preceding values together and returns the sum of those values.

The problem is that while in the loop function, numbers isn't modified with conj like I would expect it to be - numbers just stays an empty vector. Why is that?

(defn sum                        
  [number]                       
  (do (def numbers (vector))     
    (loop [iteration number]     
      (if (> iteration 0)        
        (conj numbers iteration) 
        (recur (dec iteration))))
    (map + numbers)))

Upvotes: 2

Views: 620

Answers (2)

Thumbnail
Thumbnail

Reputation: 13483

A few hints (not an answer):

  • Don't use do.
  • Use let, not def, inside a function.
  • Use the result returned by conj, or it does nothing.
  • Pass the result back through the recur.

Besides, your sum function ignores its number argument.

I think you're getting confused between number (the number of things you want to add) and numbers (the things themselves). Remember,

  • vectors (and other data structures) know how long they are; and
  • they are often, as in what follows, quickly and concisely dealt with as sequences, using first and rest instead of indexing.

The code pattern you are searching for is so common that it's been captured in a standard higher order function called reduce. You can get the effect you want by ...

(defn sum [coll] (reduce + coll))

or

(def sum (partial reduce +))

For example,

(sum (range 10))
;45

Somewhat off-topic:

If I were you, and I once was, I'd go through some of the fine clojure tutorials available on the web, with a REPL to hand. You could start looking here or here. Enjoy!

Upvotes: 4

Joseph Yourine
Joseph Yourine

Reputation: 1331

Your function does not work fro three main reasons :

  • you assumed that conj will update the value of variable numbers (but in fact it returns a copy of it bound to another name)
  • you used loop/recur pattern like in classical imperative style (it does not work the same)
  • Bad use of map

Thumbnail gave the idiomatic answer but here are correct use of your pattern :

(defn sum
  [number]
  (loop [iteration number
         numbers []]
    (if (<= iteration 0)
          (reduce + numbers)
          (recur (dec iteration) (conj numbers iteration)))))

The loop/recur pattern executes its body with updated values passed by recur. Recur updates values listed after the loop. Here, while iteration is strictly positive, recur is executed. However, when iteration reaches 0, (reduce + numbers) (actual sum) is executed on the result of multiple recursions and so the recursion ends.

Upvotes: 2

Related Questions