2015-04-09 1 views
1

У меня есть список кортежей, каждый из которых имеет длину элемента. Я использую следующий код для вычисления смещения элемента в зависимости от длины более ранних элементов.Использование функциональной формы, когда элемент списка зависит от предыдущих элементов

import pprint 
recordInfo = [(3,), (4,), (1,), (2,)] # List of lengths 
# Calculate and add offsets 
recordSize = 0 
for index, info in enumerate(recordInfo): 
    recordInfo[index] = info + (recordSize,) # Replace with new tuple with offset 
    recordSize += info[0] # Calculate next offset 
pprint.pprint(recordInfo) 

Выход

[(3, 0), (4, 3), (1, 7), (2, 8)] 

Есть ли способ, чтобы сделать петлю в функциональной форме, как список понимание? Я не могу понять, как избежать временной переменной recordSize, что делает невозможным?

+1

вы на Python 2 или 3? У Python 3 есть ['itertools.accumulate'] (https://docs.python.org/3/library/itertools.html#itertools.accumulate), который отлично решает вашу проблему. – user2357112

+0

@ user2357112 Python 3. – user694733

ответ

2

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

>>> recordInfo = [(3,), (4,), (1,), (2,)] 
>>> [info + (sum(_info[0] for _info in recordInfo[:i]),) 
     for i,info in enumerate(recordInfo)] 
[(3, 0), (4, 3), (1, 7), (2, 8)] 

Он работает путем перерасчета смещения до текущего элемента на каждой итерации, следовательно, он неэффективен.

Он работает как на Python 2 & 3.

+0

Это на самом деле довольно приятно. Я не думал использовать 'sum'. – user694733

1

Возможно, это не то, что вы подразумевали под функциональностью, но эй, всегда весело.

def increment_record(records, size=0): 
    if not records: 
     return [] 
    return [(records[0], size)] + increment_record(records[1:], size=size+records[0]) 

Но да, я не думаю, что это действительно шаблон питона позволяет человеку решать в порядке, без локального состояния (если, конечно, вы не используете функцию высокого уровня, как в вышеупомянутом itertools.aggregate, что только скрывает состояние). Конечно, если вам действительно нужно, вы можете просто определить какой-то счетный объект (или использовать закрытие).

class Tallier(object): 
    def __init__(self, val): 
      self._val = val 

    def tally(self, new_val): 
      old_val = self._val 
      self._val += new_val 
      return old_val 

[(val, tallier.tally(val)) for val in values] 
+0

Да, не совсем то, что я имел в виду. Я хочу избежать рекурсии. – user694733

2
>>> recordInfo = [3, 4, 1, 2] 

>>> from functools import reduce 
>>> reduce(lambda x, y: x + [(y, sum(x[-1]))], recordInfo, [(0, 0)])[1:] 
[(3, 0), (4, 3), (1, 7), (2, 8)] 

>>> from itertools import accumulate 
>>> list(zip(recordInfo, [0] + list(accumulate(recordInfo)))) 
[(3, 0), (4, 3), (1, 7), (2, 8)] 

Если у вас есть кортежи:

>>> recordInfo = [(3, 'a'), (4, 'b'), (1, 'c'), (2, 'd')] 

>>> reduce(lambda x, y: x + [y + (x[-1][0] + x[-1][-1],)], recordInfo, [(0,)])[1:] 
[(3, 'a', 0), (4, 'b', 3), (1, 'c', 7), (2, 'd', 8)] 

>>> from operator import itemgetter 
>>> [x + (c,) for x, c in zip(recordInfo, accumulate(map(itemgetter(0), [(0,)] + recordInfo)))] 
[(3, 'a', 0), (4, 'b', 3), (1, 'c', 7), (2, 'd', 8)] 
+0

Работает ли это с кортежами? В моих реальных кортежах кода в 'recordInfo' фактически есть больше данных перед операцией. – user694733

+0

Вы хотите сохранить исходные данные и просто добавить накопленное значение? – poke

+0

Вид, да. В исходных данных каждый кортеж имеет 3 поля, а после операции в каждом кортеже есть 4 поля. Я просто удалил 2 несвязанных поля, чтобы мой вопрос был простым. – user694733

Смежные вопросы