Reputation: 26932
You know the handy reduce
function in Python. For example, you could use it to sum up a list like so (pretend there isn't the built-in sum):
reduce(lambda x,y: x+y, [1,2,3,4], 0)
which returns (((0+1)+2)+3)+4 = 10.
Now what if I wanted a list of the intermediate sums? In this case, [1,3,6,10]
.
Here's an ugly solution. Is there something more pythonic?
def reducelist(f, l, x):
out = [x]
prev = x
for i in l:
prev = f(prev, i)
out.append(prev)
return out
Upvotes: 14
Views: 3048
Reputation: 5046
Note: Somehow I missed @DSM's answer before I wrote this. Go read and up vote that one instead, I just did. Come back if you want a longer answer.
Python has this, it's called accumulate and it's implemented in the itertools standard library module starting in Python 3.2. The optional second argument, 'func', was added in 3.3.
import itertools
l = [1,2,3,4]
out = itertools.accumulate(l)
In this case out
is an iterable. If you need a list, then
out = list(itertools.accumulate(l))
The accumulate()
function can be used to generate a running total, or 'accumulated sums'. The default function is addition. We can pass in a function as the second argument:
import itertools
import operator
l = [1,2,3,4]
factorial = itertools.accumulate(l, operator.mul)
Here we pass in operator.mul for multiplication to generate a running product. The operator
module exports a set of efficient functions corresponding to the intrinsic operators of Python.
Of course we're not limited to functions defined in the operator
module. You can use any function that accepts 2 parameters of the type of the elements in the first parameter. You can get creative, but here I'll do the opposite and explicitly implement the default addition/sum behavior using a lambda:
import itertools
l = [1,2,3,4]
out = itertools.accumulate(l, lambda a, b: a + b)
Finally, since you asked, I think using accumulate
is more Pythonic then your looping example.
Upvotes: 1
Reputation: 16403
If you make your solution to a generator it's shorter and it better obeys functional programming style. I would add an default value of 0 for x too:
def reducelist(f, lst, x=0):
prev = x
for i in lst:
prev = f(prev, i)
yield prev
That is definitely more pythonic.
Upvotes: 9
Reputation: 353059
My favourite, if you're recent enough:
Python 3.2.1 (default, Jul 12 2011, 22:22:01)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import itertools
>>> itertools.accumulate([1,2,3,4])
<itertools.accumulate object at 0x1006baad0>
>>> list(itertools.accumulate([1,2,3,4]))
[1, 3, 6, 10]
accumulate also accepts a function argument [even more recent, though-- 3.3]:
>>> list(itertools.accumulate([1,2,3,4], lambda x,y: x+y))
[1, 3, 6, 10]
>>> list(itertools.accumulate([1,2,3,4], lambda x,y: x+y+1))
[1, 4, 8, 13]
Upvotes: 18