开发者

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]
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜