I understand the conceptual difference between reduce
and apply
:
(reduce + (list 1 2 3 4 5))
; translates to: (+ (+ (+ (+ 1 2) 3) 4) 5)
(apply + (list 1 2 3 4 5))
; translates to: (+ 1 2 3 4 5)
However, which one is more idiomatic clojure? Does it make much difference one way or the other? From my (limited) performance testing, it seems reduce
is a bit faster.
reduce
andapply
are of course only equivalent (in terms of the ultimate result returned) for associative functions which need to see all their arguments in the variable-arity case. When they are result-wise equivalent, I'd say thatapply
is always perfectly idiomatic, whilereduce
is equivalent -- and might shave off a fraction of a blink of an eye -- in a lot of the common cases. What follows is my rationale for believing this.+
is itself implemented in terms ofreduce
for the variable-arity case (more than 2 arguments). Indeed, this seems like an immensely sensible "default" way to go for any variable-arity, associative function:reduce
has the potential to perform some optimisations to speed things up -- perhaps through something likeinternal-reduce
, a 1.2 novelty recently disabled in master, but hopefully to be reintroduced in the future -- which it would be silly to replicate in every function which might benefit from them in the vararg case. In such common cases,apply
will just add a little overhead. (Note it's nothing to be really worried about.)On the other hand, a complex function might take advantage of some optimisation opportunities which aren't general enough to be built into
reduce
; thenapply
would let you take advantage of those whilereduce
might actually slow you down. A good example of the latter scenario occuring in practice is provided bystr
: it uses aStringBuilder
internally and will benefit significantly from the use ofapply
rather thanreduce
.So, I'd say use
apply
when in doubt; and if you happen to know that it's not buying you anything overreduce
(and that this is unlikely to change very soon), feel free to usereduce
to shave off that diminutive unnecessary overhead if you feel like it.For newbies looking at this answer,
be careful, they are not the same:
Opinions vary- In the greater Lisp world,
reduce
is definitely considered more idiomatic. First, there is the variadic issues already discussed. Also, some Common Lisp compilers will actually fail whenapply
is applied against very long lists because of how they handle argument lists.Amongst Clojurists in my circle, though, using
apply
in this case seems more common. I find it easier to grok and prefer it also.It doesn't make a difference in this case, because + is a special case that can apply to any number of arguments. Reduce is a way to apply a function that expects a fixed number of arguments (2) to an arbitrarily long list of arguments.
I normally find myself preferring reduce when acting on any kind of collection - it performs well, and is a pretty useful function in general.
The main reason I would use apply is if the parameters mean different things in different positions, or if you have a couple of initial parameters but want to get the rest from a collection, e.g.
In this specific case I prefer
reduce
because it's more readable: when I readI know immediately that you're turning a sequence into a value.
With
apply
I have to consider which function is being applied: "ah, it's the+
function, so I'm getting... a single number". Slightly less straightforward.When using a simple function like +, it really doesn't matter which one you use.
In general, the idea is that
reduce
is an accumulating operation. You present the current accumulation value and one new value to your accumulating function The result of the function is the cumulative value for the next iteration. So, your iterations look like:For apply, the idea is that you are attempting to call a function expecting a number of scalar arguments, but they are currently in a collection and need to be pulled out. So, instead of saying:
we can say:
and it is converted to be equivalent to:
So, using "apply" is like "removing the parentheses" around the sequence.
Bit late on the topic but I did a simple experiment after reading this example. Here is result from my repl, I just can't deduce anything from the response, but seems there is some sort of caching kick in between reduce and apply.
Looking at source code of clojure reduce its pretty clean recursion with internal-reduce, didn't found anything on implementation of apply though. Clojure implementation of + for apply internally invoke reduce, which is cached by repl, which seem to explain the 4th call. Can someone clarify whats really happening here?
The beauty of apply is given function (+ in this case) can be applied to argument list formed by pre-pending intervening arguments with an ending collection. Reduce is an abstraction to process collection items applying the function for each and doesn't work with variable args case.