As far as I understand, the reduce function takes a list l
and a function f
. Then, it calls the function f
on first two elements of th
From the Python reduce documentation,
reduce(function, sequence) returns a single value constructed by calling the (binary) function on the first two items of the sequence, then on the result and the next item, and so on.
So, stepping through. It computes reduce_func
of the first two elements, reduce_func(1, 3) = 1! * 3! = 6
. Then, it computes reduce_func
of the result and the next item: reduce_func(6, 1) = 6! * 1! = 720
.
You missed that, when the result of the first reduce_func
call is passed as input to the second, it's factorialized before the multiplication.
Beyond the trivial examples, here is one where I find reduce
to be actually quite useful:
Imagine an iterable of ordered int
values, often with some runs of contiguous values, and that we'd like to "summarize" it as a list of tuples representing ranges. (Note also that this iterable could be a generator of a very long sequence --another reason to use reduce
and not some operation on an in-memory collection).
from functools import reduce
def rle(a, b):
if a and a[-1][1] == b:
return a[:-1] + [(a[-1][0], b + 1)]
return a + [(b, b + 1)]
reduce(rle, [0, 1, 2, 5, 8, 9], [])
# [(0, 3), (5, 6), (8, 10)]
Notice the use of a proper initial
value ([]
here) for reduce
.
Corner cases handled as well:
reduce(rle, [], [])
# []
reduce(rle, [0], [])
# [(0, 1)]
Your function calls fact()
on both arguments. You are calculating ((1! * 3!)! * 1!)
. The workaround is to only call it on only the second argument, and pass reduce()
an initial value of 1.
Ok, got it:
I need to map the numbers to their factorials first and then call reduce with multiply operator.
So, this would work:
lst_fact = map(fact, lst)
reduce(operator.mul, lst_fact)
Well, first of all, your reduce_func
doesn't have the structure of a fold; it doesn't match your description of a fold (which is correct).
The structure of a fold is: def foldl(func, start, iter): return func(start, foldl(func, next(iter), iter)
Now, your fact
function doesn't operate on two elements - it just calculates factorial.
So, in sum, you're not using a fold, and with that definition of factorial, you don't need to.
If you do want to play around with factorial, check out the y-combinator: http://mvanier.livejournal.com/2897.html
If you want to learn about folds, look at my answer to this question, which demonstrates its use to calculate cumulative fractions: creating cumulative percentage from a dictionary of data
The easiest way to understand reduce() is to look at its pure Python equivalent code:
def myreduce(func, iterable, start=None):
it = iter(iterable)
if start is None:
try:
start = next(it)
except StopIteration:
raise TypeError('reduce() of empty sequence with no initial value')
accum_value = start
for x in iterable:
accum_value = func(accum_value, x)
return accum_value
You can see that it only makes sense for your reduce_func() to apply the factorial to the rightmost argument:
def fact(n):
if n == 0 or n == 1:
return 1
return fact(n-1) * n
def reduce_func(x,y):
return x * fact(y)
lst = [1, 3, 1]
print reduce(reduce_func, lst)
With that small revision, the code produces 6 as you expected :-)