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

雨燕双飞 提交于 2019-12-13 21:38:12

问题


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?


回答1:


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)))



回答2:


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.



来源:https://stackoverflow.com/questions/35145965/adding-the-elements-of-a-vector-without-using-reduce-or-apply

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!