这个累加总和有什么问题?

4
我试图得到[1,3,6]作为结果。我是否错过了一些非常明显的东西?我收到的错误是: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

a = [1,2,3]
cumulative_sum(a)

7
您需要阅读教程。 - SilentGhost
2
你需要学习“打印”调试。 - AndiDog
1
迭代容器中的项目(使用“for i in some_container:”)和迭代列表(或数组或其他数字索引对象)有效索引范围(使用“for i in range(len(some_list)):”)之间有区别。 - Jim Dennis
5个回答

8
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)

在Python中,数组是以0为基础的,因此当你将n[i]i混淆时,你实际上访问了n[3],而n只从0到2。


实际上,print 语句应该是 return cum_sum,对吧? - Kimvais
@Kimvais:我想,如果他确实想要返回它而不是打印出来,他说“打印”,所以我也说“打印”。 - Ken Bloom
是的 - 但他在问“出了什么问题” :) - 我“猜测”该函数只应该计算累积和,而不是打印它。 - Kimvais

7
问题出在您的循环中:
for i in n:
    y += n[i]
for循环正在迭代n,而不是索引。将y += n[i]更改为y += i
第三次循环时(即i为3时),会引发异常,因为3不在数组的范围内(有效索引为[0-2])。
如果您还想遍历索引,请使用内置的enumerate函数:
for i, x in enumerate(n):
    assert n[i] == x

1
在循环中,他想要在每次迭代中添加 i,而不是 n。因此不是 y += n,而是 y += i - eumiro
@eumiro:哇,我当时在想什么啊!那就是我的意思,但不是我写的。发现得好,谢谢! - Cameron

6
这里是一个基于生成器的简单实现:
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))]

我认为这是实现cumsum的非常Pythonic的方法。

但是这里有一个更实用的实现,可以处理几乎所有可能存在加法的类型。

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', )))]

因此,所有这些示例都表现出了预期的方式,但使用更加“Pythonic”的版本则不是如此。请自行尝试并找出不同行为的原因。

更新:
根据评论,一个更通用的累加和可以像这样:

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))

仍然有两个值,但我认为它仍然非常易读。现在的实现已经强调了可迭代对象的第一个元素类型会决定如何处理其余元素的加法操作。


写一个生成器会加分,但是不需要将c初始化为seq[0]然后创建从1开始的索引范围,只需迭代序列即可:cum = 0; for c in seq: cum += c; yield cum。不需要两个yield,也不需要索引访问,使用迭代器,在不支持索引访问的序列中工作。 - PaulMcG
@Paul McGuire:请注意,您评论的内容已经在我的第一个cumsum中实现。但是,请尝试将其与我第二个实现相关的所有示例结合起来,这样您就可以看到区别了。即使第一个也有点像Python,但第二个可以处理更大范围的数据。我从我刚开始学习Python基础时的旧笔记中找到了这个。在我看来,这两个实现都是简单但有价值的Python学习示例。谢谢。 - eat
@Paul McGuire:我们需要的是一种方法,将两种实现的优点结合起来。请看我的回答。 - John Machin

1
这里有一个足够强大的函数,适用于任何支持+的对象可迭代,并且适用于从2.3版本开始的任何Python(只需调整printxrange以使测试基础设施与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]
>>>

一个足够强大的函数,适用于任何可迭代对象,不完全正确。例如考虑输入 ['a', 1]。我的代码也有同样的“缺陷”,但这不是我想要表达的重点。更多的是关于 += 在可变和不可变类型上操作的语义。此外,我不认为在循环中进行不必要的测试是有价值的 ;-)。谢谢。 - eat
@eat:你漏掉了我写的一部分:“在支持+的对象上”。关于“循环中不必要的测试”:我期待看到你对可迭代而不是序列以及任意“可加”类型而不仅仅是数字的组合的解决方案。顺便说一下,当输入一个空序列时,你的第二段代码会出错。 - John Machin
我的意图并不是贬低你的代码,只是指出使用cumsum可能会出现什么样的语义问题。是的,我的第二个cumsum没有考虑到空值。但我认为我现在在我的答案更新中有一个相当合理的实现。还有什么想法可以分享吗?谢谢。 - eat
@eat: 你的更新并不无理 :-) 顺便说一下,使用xrange()作为测试案例并不能证明什么;xrange支持下标操作,并且会与你的第二段代码一起工作。试试 o = xrange(4000, 5000); print o[100]; print o[10]; print len(o) - John Machin

0
for I in n:
    # I will be an item from n
    y+=I

或者你尝试做了什么:

for i in range(len(n)):
    # i is an int that you can index with
    y+=n[i]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接