Yar
Yar

Reputation: 7457

Adding the elements of a Vector without using `reduce` or `apply`

So I am trying to re-implement the reduce method, so it can add a couple of numbers that normally can be done using reduce, like:

(reduce + [1 2 3]) ;; 6
(newRd + [1 2 3])  ;; 6

So I thought maybe it can be done using a recursive function that adds the first element of the vector every time it is called and do it again for the rest of the vector. Something like this:

(defn newRd [list]
  (let [sum 0]
    (if (not= 0 (count list))
      (+ sum (first list))
      (newRd (rest list))
      )
    )
  )

I think I am not doing local storing correctly. Any suggestion or maybe a better approach?

Upvotes: 0

Views: 590

Answers (2)

Thumbnail
Thumbnail

Reputation: 13473

Further to leetwinksi's answer ...

You might as well implement new-reduce (camel case is not idiomatic) in general:

(defn new-reduce
  ([f init coll]
   (if (seq coll)
     (recur f (f init (f init (first coll))) (rest coll))
     init))
  ([f coll]
   (if (seq coll)
     (reduce f (first coll) (rest coll))
     (f))))

Then

(new-reduce + [1 2 3]) ;; 6

This is more or less what the source code for reduce looked like until recently, if you stripped chunking out.

The two-argument version that you use leans on the three-argument version, which you can recur on directly, without an explicit loop. This entails passing f each time, but that's what it used to do. Presumably it's faster to carry an extra argument than to work in a local scope.

Upvotes: 2

leetwinski
leetwinski

Reputation: 17859

there are two mistakes here in your code:

1) you don't add your current sum to the recursive call result

2) you should return zero when the list is empty

corrected variant:

(defn newRd [list]
  (let [sum 0]
    (if (not= 0 (count list))
      (+ sum (first list)
         (newRd (rest list)))
      sum)))

in repl:

user> (newRd [1 2 3 4])
10

next, you can update it a bit:

first you don't really need the sum in let statement, since the sum always = 0

second, there is a lib function empty? to check if list is empty.

(defn newRd [list]
  (if-not (empty? list)
    (+ (first list)
       (newRd (rest list)))
    0))

but remember: clojure doesnt'do tail call optimization, so it is easy to cause stack owerflow with a long list:

user> (newRd (repeat 1000000 1))
StackOverflowError   user/newRd (form-init289434844644272272.clj:73)

so it's preferable to use loop/recur

(defn sum-list [list]
  (loop [list list sum 0]
    (if (empty? list)
      sum
      (recur (rest list) (+ sum (first list))))))

in repl:

user> (sum-list (repeat 1000000 1))
1000000

the other option is to make the function itself tail recursive:

(defn newRd [list sum]
  (if-not (empty? list)
    (recur (rest list) (+ sum (first list)))
    sum))

user> (newRd (repeat 1000000 1) 0)
1000000

then you can add the additoinal arity, for not to pass the second parameter in every call:

(defn newRd
  ([list] (newRd list 0))
  ([list sum]
   (if-not (empty? list)
     (recur (rest list) (+ sum (first list)))
     sum)))

Upvotes: 10

Related Questions