Ankur's answer is correct. Also, I think this link explains things very well:
http://www.learningclojure.com/2010/08/reduce-not-scary.html
Now, to answer your questions...
Yes. The second argument to reduce
is the initial value of the accumulator.
Yes, it is always an accumulator. The whole point of reduce
is that it lets you do accumulation in a functional and immutable way.
I will note that it is possible to use reduce
in a way where the accumulation does not matter, like in mikera's answer. In that case, reduce
is still doing the accumulation (internally), but it uses the same value over and over again, so it has no noticeable effect.
When calling reduce
with only two arguments... the rules that Clojure uses are a little complex, but what it boils down to is that this...
(reduce + [1 2 3])
...will use the first element of the sequence as the initial value, which means it's the same as this:
(reduce + 1 [2 3])
You asked about what an accumulator is. An accumulator is the concept of accumulating data while looping over something.
In imperative languages, an accumulator is generally a variable that is mutated while looping. Let's look at Leonel's example, slightly modified:
var sum = 0;
for (var i = 0; i < 10; ++i) {
sum = sum + i;
}
return sum;
At first, this would seem to be impossible to do in a functional way. But with reduce
, you can!
(reduce (fn [sum i] (+ sum i)) 0 (range 0 10))
How this works is, reduce
takes three arguments:
- A transformation function
- The initial value of the accumulator
- A sequence
It calls the transformation function with two arguments:
sum
is the current value of the accumulator
i
is the current element of the sequence
Now, whatever the transformation function returns is used as the current value of the accumulator. In other words... on the first iteration sum
will be the initial value. After the first iteration, sum
is whatever the transformation function returned on the previous iteration.
Perhaps it will help if I write an implementation of reduce
in JavaScript that uses mutation:
function reduce(f, init, coll) {
for (var i = 0; i < coll.length; ++i) {
init = f(init, coll[i]);
}
return init;
}
reduce(function (sum, i) { return sum + i }, 0, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
As you can see, the reduce
function looks very similar to the imperative code from before.
Now let's implement reduce
in a functional way, without mutation, in Clojure:
(defn reduce [f init coll]
(if-let [s (seq coll)]
(reduce f (f init (first s)) (rest s))
init))
(reduce (fn [sum i] (+ sum i)) 0 (range 0 10))
But regardless of whether reduce
does the accumulation in a mutable or immutable way, it is doing an accumulation.
For funsies, it's interesting to note that Clojure implements reverse
using reduce
:
(defn reverse
"Returns a seq of the items in coll in reverse order. Not lazy."
{:added "1.0"
:static true}
[coll]
(reduce1 conj () coll))
Figuring out why this works is an interesting mental exercise.
You can also do neat stuff like implementing map, filter, and other stuff using reduce. But I think that goes a bit beyond your question.