Список понимания для промежуточного итога

Я хочу получить промежуточную сумму из списка чисел.

В демонстрационных целях я начну с последовательного списка чисел, используя range

a = range(20)

runningTotal = []
for n in range(len(a)):
    new = runningTotal[n-1] + a[n] if n > 0 else a[n]
    runningTotal.append(new)

# This one is a syntax error
# runningTotal = [a[n] for n in range(len(a)) if n == 0 else runningTotal[n-1] + a[n]]

for i in zip(a, runningTotal):
    print "{0:>3}{1:>5}".format(*i)

приводит к

  0    0
  1    1
  2    3
  3    6
  4   10
  5   15
  6   21
  7   28
  8   36
  9   45
 10   55
 11   66
 12   78
 13   91
 14  105
 15  120
 16  136
 17  153
 18  171
 19  190

Как видите, я инициализирую пустой список [], затем append () в каждой итерации цикла. Есть ли более элегантный способ для этого, например, для понимания списка?

24
задан alain.janinm 1 May 2012 в 09:53
поделиться

7 ответов

У понимания списка нет хорошего (чистого, переносимого) способа ссылаться на сам список, который он строит. Одним из хороших и элегантных подходов может быть выполнение работы в генераторе:

def running_sum(a):
  tot = 0
  for item in a:
    tot += item
    yield tot

чтобы получить это в виде списка, конечно, используйте list(running_sum(a)).

28
ответ дан 28 November 2019 в 22:12
поделиться

В Python это можно реализовать в 2 строки.

Использование параметра по умолчанию устраняет необходимость поддерживать внешнюю вспомогательную переменную, а затем мы просто делаем отображение в список.

def accumulate(x, l=[0]): l[0] += x; return l[0];
map(accumulate, range(20))
10
ответ дан 28 November 2019 в 22:12
поделиться

Я бы использовал для этого сопрограмму:

def runningTotal():
    accum = 0
    yield None
    while True:
        accum += yield accum

tot = runningTotal()
next(tot)
running_total = [tot.send(i) for i in xrange(N)]
1
ответ дан 28 November 2019 в 22:12
поделиться

Это неэффективно, так как это происходит каждый раз с самого начала, но возможно, что это так:

a = range(20)
runtot=[sum(a[:i+1]) for i,item in enumerate(a)]
for line in zip(a,runtot):
    print line
0
ответ дан 28 November 2019 в 22:12
поделиться

Если вы можете использовать numpy, в нем есть встроенная функция cumsum, которая делает следующее.

import numpy
tot = numpy.cumsum(a)  # returns a numpy.ndarray
tot = list(tot)        # if you prefer a list
26
ответ дан 28 November 2019 в 22:12
поделиться

Я не уверен насчет "элегантности", но я думаю, что следующее намного проще и интуитивно понятнее (ценой дополнительной переменной):

a = range(20)

runningTotal = []

total = 0
for n in a:
  total += n
  runningTotal.append(total)

Функциональный способ сделать то же самое:

a = range(20)
runningTotal = reduce(lambda x, y: x+[x[-1]+y], a, [0])[1:]

... но это намного менее читаемо/поддерживаемо и т.д.

@Omnifarous предлагает улучшить это до:

a = range(20)
runningTotal = reduce(lambda l, v: (l.append(l[-1] + v) or l), a, [0])

... но я все еще нахожу это менее понятным, чем мое первоначальное предложение.

Вспомните слова Кернигана: "Отладка вдвое сложнее, чем написание кода. Поэтому, если вы пишете код настолько умно, насколько это возможно, вы, по определению, недостаточно умны для его отладки"

.
8
ответ дан 28 November 2019 в 22:12
поделиться

Вам нужны две вещи: сворачивание (сокращение) и забавная функция, которая хранит список результатов другой функции, которую я назвал запущенной. Я делал версии как с начальным параметром, так и без него; в любом случае они должны быть сокращены с начальным [].

def last_or_default(list, default):
    if len(list) > 0:
        return list[-1]
    return default

def initial_or_apply(list, f, y):
    if list == []:
        return [y]
    return list + [f(list[-1], y)]

def running_initial(f, initial):
    return (lambda x, y: x + [f(last_or_default(x,initial), y)])

def running(f):
    return (lambda x, y: initial_or_apply(x, f, y))

totaler = lambda x, y: x + y
running_totaler = running(totaler)
running_running_totaler = running_initial(running_totaler, [])

data = range(0,20)
running_total = reduce(running_totaler, data, [])
running_running_total = reduce(running_running_totaler, data, [])

for i in zip(data, running_total, running_running_total):
    print "{0:>3}{1:>4}{2:>83}".format(*i)

Это займет много времени в действительно больших списках из-за оператора +. На функциональном языке, если все сделано правильно, это построение списка будет O (n).

Вот несколько первых строк вывода:

0   0                      [0]
1   1                   [0, 1]
2   3                [0, 1, 3]
3   6             [0, 1, 3, 6]
4  10         [0, 1, 3, 6, 10]
5  15     [0, 1, 3, 6, 10, 15]
6  21 [0, 1, 3, 6, 10, 15, 21]
0
ответ дан 28 November 2019 в 22:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: