2017-01-20 18 views
-1

Моя актуальная проблема гораздо больше, но суть его есть что-то вроде:Itertools/функциональный подход к обработке состояния над объектом?

def rec_func(it, newit, idx=0, seen_5=False, even_seen=0): 
    num = next(it, None) 
    if num is None: 
     return newit 
    elif num == 5: seen_5 = True 
    elif num&1==0: even_seen +=1 
    else: num*=20 
    newit.append(num) 
    print '[{}] seen_5 = {}, even_seen = {}'.format(idx, seen_5, even_seen) 
    return rec_func(it, newit, idx+1, seen_5, even_seen) 

Выполнить с (http://ideone.com/4kBbhn):

a = [] 
rec_func(iter(xrange(10)), a) 
print 'a =', a 

Но есть некоторые серьезные проблемы с этим дизайном, рекурсия ISN что эффективный в Python и список создается и передается только для его результата (который не является генератором). Попытка получить это:

a = tuple(itertools.<something>(func, iter(xrange(10))) 
print a # same answer as before 

Как делать я? - Все, что я могу думать:

def func(num, state): 
    if num == 5: state['seen_5'] = True 
    elif num&1==0: state['even_seen'] +=1 
    else: num*=20 
    print '[?] seen_5 = {}, even_seen = {}'.format(state['seen_5'], 
                state['even_seen']) 
    return num 

_state = {'seen_5': False, 'even_seen': 0} 
print tuple(imap(partial(func, state=_state), xrange(10))) 

Который работает, но, кажется, вид ужасный ... есть функциональное решение?

+1

Если вы хотите сохранить состояние, то почему бы не написать класс, создать экземпляр, а затем сопоставить метод над iterable? Затем метод может хранить любое состояние, которое ему нужно для экземпляра. Попытка сделать это просто с функцией кажется бесполезно ограничительной. – BrenBarn

+0

@BrenBarn Должна быть причина, так как OP помечен как «чисто функциональный». –

+0

Я отредактировал пример, проходящий в объекте, который хранит свою собственную память (поэтому передает по ссылке в Python). Это уродливо, и я предпочел бы функциональное решение, вы знаете об этом? - Может быть, использовать аккумуляторы? –

ответ

1

Один из способов избавиться от рекурсии и промежуточные списки использует генератор:

from __future__ import print_function 


def gen(it): 
    idx = 0 
    seen_5 = False 
    even_seen = 0 
    for num in it: 
     if num == 5: 
      seen_5 = True 
     elif num&1==0: 
      even_seen +=1 
     else: 
      num *= 20    
     print('[{}] seen_5 = {}, even_seen = {}'.format(idx, seen_5, even_seen)) 
     idx += 1 
     yield num 

print('a =', list(gen(iter(range(10))))) 

Выход:

[0] seen_5 = False, even_seen = 1 
[1] seen_5 = False, even_seen = 1 
[2] seen_5 = False, even_seen = 2 
[3] seen_5 = False, even_seen = 2 
[4] seen_5 = False, even_seen = 3 
[5] seen_5 = True, even_seen = 3 
[6] seen_5 = True, even_seen = 4 
[7] seen_5 = True, even_seen = 4 
[8] seen_5 = True, even_seen = 5 
[9] seen_5 = True, even_seen = 5 
a = [0, 20, 2, 60, 4, 5, 6, 140, 8, 180] 
+0

Правда, это чистое решение. Хм, я сегодня пытался «уступить», а я знаю, что я сделал не так! - Я положил 'yield' внутри функции, которую я вызвал, чтобы перебрать его. Очевидно, это решение будет работать, поэтому спасибо.- Отказаться от принятия сейчас, хотят посмотреть, что другие придумали. - +1 –

+0

Это хорошее решение, но это довольно далеко не чисто функционально. –

+0

Да @ Rightleg - чистое функциональное решение [best] будет принято в качестве ответа –