What's wrong with this cumulative sum?
I'm trying to get [1,3,6] as the result. Am I missing something really obvious? The error I got is: IndexError: list index out of range
def cumulative_sum(n):
cum_sum = []
y = 0
for i in n:
y += n[i]
cum_sum.append(y)
print cum_sum
开发者_Go百科a = [1,2,3]
cumulative_sum(a)
def cumulative_sum(n):
cum_sum = []
y = 0
for i in n: # <--- i will contain elements (not indices) from n
y += i # <--- so you need to add i, not n[i]
cum_sum.append(y)
print cum_sum
a = [1,2,3]
cumulative_sum(a)
Arrays are zero-based in Python, so when you confused n[i]
with i
, you were accessing n[3]
while n
only goes from 0 to 2.
The problem is with your loop:
for i in n:
y += n[i]
The for
loop is iterating over the values of n
, not the indexes. Change y += n[i]
to y += i
.
The exception is raised on the third pass through the loop (when i is 3), since 3 is not in the bounds of the array (valid indexes are [0-2]).
If you want to loop over the indexes as well, you can use the built-in enumerate
function:
for i, x in enumerate(n):
assert n[i] == x
Here is a simple generator based implementation:
def cumsum(seq):
s= 0
for c in seq:
s+= c
yield s
print [c for c in cumsum(range(7))]
print [c for c in cumsum((0, 1, 2, 3, 4, 5, 6))]
Which is IMHO quite Pythonic way to implement cumsum.
But here is a more pragmatic implementation, which allows you to handle (allmost) all types where addition may make sense.
def cumsum(seq):
s= seq[0]
for k in xrange(1, len(seq)):
yield s
s= s+ seq[k]
yield s
print [c for c in cumsum(range(7))]
print [c for c in cumsum((0, 1, 2, 3, 4, 5, 6))]
print [c for c in cumsum(['a', 'b', 'c'])]
print [c for c in cumsum([['a'], ['b'], ['c']])]
print [c for c in cumsum((('a', ), ('b', ), ('c', )))]
So all of these examples behaves expected way, which is not true with the more Pythonic
version. Try it out yourself and figure out the reason for different behaviour.
Update:
Based on comments, a more generic cumsum would be like:
def cumsum(iterable):
iterable= iter(iterable)
s= iterable.next()
yield s
for c in iterable:
s= s+ c
yield s
tests= [
[],
[1],
[1, 2],
range(7),
(0, 1, 2, 3, 4, 5, 6),
['a', 'b', 'c'],
[['a'], ['b'], ['c']],
(('a', ), ('b', ), ('c', )),
xrange(7),
]
for test in tests:
print test, '=> ', list(cumsum(test))
Still two yields, but IMHO it's still very readable. And the implementation has now the emphasis that the type of the first element of iterable dictates how addition is expected to behave with the rest of elements.
Here's a robust enough function that works on any iterable over objects that support +
and on any Python from 2.3 onwards (just fiddle with the print
and xrange
to make the test infrastructure work with 3.x):
Python 2.3.5 (#62, Feb 8 2005, 16:23:02) [MSC v.1200 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> def cumsum(iterable):
... first = True
... for v in iterable:
... if first:
... tot = v
... first = False
... else:
... tot = tot + v
... yield tot
...
>>> def squares(start, stop):
... for i in xrange(start, stop):
... yield i * i
...
>>> tests = [
... [],
... [1],
... [1, 2],
... range(7),
... (0, 1, 2, 3, 4, 5, 6),
... ['a', 'b', 'c'],
... [['a'], ['b'], ['c']],
... (('a', ), ('b', ), ('c', )),
... squares(1, 5),
... ]
>>>
>>> for test in tests:
... print test, list(cumsum(test))
...
[] []
[1] [1]
[1, 2] [1, 3]
[0, 1, 2, 3, 4, 5, 6] [0, 1, 3, 6, 10, 15, 21]
(0, 1, 2, 3, 4, 5, 6) [0, 1, 3, 6, 10, 15, 21]
['a', 'b', 'c'] ['a', 'ab', 'abc']
[['a'], ['b'], ['c']] [['a'], ['a', 'b'], ['a', 'b', 'c']]
(('a',), ('b',), ('c',)) [('a',), ('a', 'b'), ('a', 'b', 'c')]
<generator object at 0x014B6A58> [1, 5, 14, 30]
>>>
for I in n:
# I will be an item from n
y+=I
or what you tried to do:
for i in range(len(n)):
# i is an int that you can index with
y+=n[i]
精彩评论