2013-02-23 2 views
11

Синтаксис синтаксиса Python через свои батареи позволяет выражать выраженную строку кода в читаемых одном лайнерах. Рассмотрим следующие примерыЭквивалентная структура вложенной петли с помощью Itertools

====================================================| 
for a in range(3):         | 
    for b in range(3):        | 
     for c in range(3):       | 
      print (a,b,c),       | 
- - - - - - - - - - - - - - - - - -| 
for e in product(range(3), repeat=3):    | 
    print e,          | 
====================================================| 
for a in range(3):         | 
    for b in range(a , 3):       | 
     for c in range(b , 3):      | 
      print (a,b,c),       | 
- - - - - - - - - - - - - - - - - -| 
for e in combinations_with_replacement(range(3), 3):| 
    print e,          | 
====================================================| 
for a in range(3):         | 
    for b in range(a + 1, 3):      | 
     for c in range(b + 1, 3):     | 
      print (a,b,c),       | 
- - - - - - - - - - - - - - - - - -| 
for e in combinations(range(3), 3):     | 
    print e,          | 
====================================================| 
for a in range(3):         | 
    for b in range(3):        | 
     for c in range(3):       | 
      if len(set([a,b,c])) == 3:    | 
       print (a,b,c),      | 
- - - - - - - - - - - - - - - - - -| 
for e in permutations(range(3)):     | 
    print e,          | 
====================================================| 

Из Late я закончил с глубоким вложенной зависимой петли я пытался выразить лаконично, но не

Структура цикла будет выглядеть следующим образом

for a in A(): 
    for b in B(a): 
     for c in C(b): 
      foo(a,b,c) 

Может ли такая структура выражаться в эквивалентной нотации itertools?

+0

Просто примечание. Этот последний цикл в Prolog был бы выражен как: a (A), b (A, B), c (B, C) '. Если вам нравится играть с петлями, играйте с Prolog. – liori

+0

Используете ли вы 'a',' b' (промежуточные значения) в теле самого внутреннего цикла? – jfs

+0

@ liori: Пролог всегда завлекал меня, но никогда не был достаточно умным, чтобы изучать искусственный интеллект. – Abhijit

ответ

4

Существует нет, но вы можете сделать одно:

def chainGang(steps, currentVars=None): 
    thisOne = steps[0] 
    if currentVars is None: 
     for item in thisOne(): 
      for gang in chainGang(steps[1:], [item]): 
       yield gang 
    elif len(steps) == 1:  
     for item in thisOne(currentVars[-1]): 
      yield currentVars + [item] 
    else: 
     for item in thisOne(currentVars[-1]): 
      for gang in chainGang(steps[1:], currentVars + [item]): 
       yield gang 

И потом:

>>> outer = lambda: ["A", "B", "C", "D"] 
>>> middle = lambda letter: [letter, letter*2, letter*3] 
>>> inner = lambda s: range(len(s)+1) 
>>> for a in chainGang([outer, middle, inner]): 
...  print a 
[u'A', u'A', 0] 
[u'A', u'A', 1] 
[u'A', u'AA', 0] 
[u'A', u'AA', 1] 
[u'A', u'AA', 2] 
[u'A', u'AAA', 0] 
[u'A', u'AAA', 1] 
[u'A', u'AAA', 2] 
[u'A', u'AAA', 3] 
[u'B', u'B', 0] 
[u'B', u'B', 1] 
[u'B', u'BB', 0] 
[u'B', u'BB', 1] 
[u'B', u'BB', 2] 
[u'B', u'BBB', 0] 
[u'B', u'BBB', 1] 
[u'B', u'BBB', 2] 
[u'B', u'BBB', 3] 
[u'C', u'C', 0] 
[u'C', u'C', 1] 
[u'C', u'CC', 0] 
[u'C', u'CC', 1] 
[u'C', u'CC', 2] 
[u'C', u'CCC', 0] 
[u'C', u'CCC', 1] 
[u'C', u'CCC', 2] 
[u'C', u'CCC', 3] 
[u'D', u'D', 0] 
[u'D', u'D', 1] 
[u'D', u'DD', 0] 
[u'D', u'DD', 1] 
[u'D', u'DD', 2] 
[u'D', u'DDD', 0] 
[u'D', u'DDD', 1] 
[u'D', u'DDD', 2] 
[u'D', u'DDD', 3] 
5

Там нет точного itertools решения, но простое сочетание itertools функций будет достаточно:

def chain_imap_accumulate(seq, f): 
    def acc_f(x): 
     for n in f(x[-1]): 
      yield x + (n,) 
    return chain.from_iterable(imap(acc_f, seq)) 

def accumulative_product(*generators): 
    head, tail = generators[0], generators[1:] 
    head = imap(tuple, head()) 
    return reduce(chain_imap_accumulate, tail, head) 

Быстрый тест. Определения:

from itertools import chain, imap, izip 
chain_ = chain.from_iterable 

def A(): 
    yield 'A' 
    yield 'B' 

def B(x): 
    yield int(x, 16) 
    yield int(x, 16) + 1 

def C(x): 
    yield str(x) + 'Z' 
    yield str(x) + 'Y' 

И результат:

>>> list(accumulative_product(A, B, C)) 
[('A', 10, '10Z'), ('A', 10, '10Y'), 
('A', 11, '11Z'), ('A', 11, '11Y'), 
('B', 11, '11Z'), ('B', 11, '11Y'), 
('B', 12, '12Z'), ('B', 12, '12Y')] 

Почти вся сложность происходит от накопления входов, как быстрый «вывод» из приведенного выше кода показывает. Окончательный (c) значения могут быть получены с использованием только пару вложенных itertools конструкций:

>>> list(chain_(imap(C, chain_(imap(B, (A())))))) 
['10Z', '10Y', '11Z', '11Y', '11Z', '11Y', '12Z', '12Y'] 

Это может быть обобщена с reduce. Для работы с reduce, chain_imap не может использовать стандартный аргумент аргумента imap. Он должен быть заменен:

def chain_imap(seq, f): 
    return chain.from_iterable(imap(f, seq)) 

Это дает те же результаты:

>>> list(reduce(chain_imap, [B, C], A())) 
['10Z', '10Y', '11Z', '11Y', '11Z', '11Y', '12Z', '12Y'] 

Последняя задача накапливающиеся начальные значения, так что у вас есть доступ к a, b и c. Это займет немного мысли, чтобы получить права, но реализация довольно проста - мы просто должны преобразовать f в функцию, которая игнорирует все входные значения, но последний, и добавляет новые значения полного ввода:

def chain_imap_accumulate(seq, f): 
    def acc_f(x): 
     for n in f(x[-1]): 
      yield x + (n,) 
    return chain.from_iterable(imap(acc_f, seq)) 

Это требует, чтобы первые входы завернуть в кортежах, поэтому мы отображаем A с tuple:

>>> list(reduce(chain_imap_accumulate, [B, C], imap(tuple, A()))) 
[('A', 10, '10Z'), ('A', 10, '10Y'), 
('A', 11, '11Z'), ('A', 11, '11Y'), 
('B', 11, '11Z'), ('B', 11, '11Y'), 
('B', 12, '12Z'), ('B', 12, '12Y')] 

Перепишите выше для ясности, а код в верхней части этого результатов ответа.

Кстати, chain_imap_accumulate можно переписать несколько более коротко, используя ген.Это можно комбинировать с более короткой версией accumulative_product для очень компактного определения (если вас это интересует). Это также случается, чтобы полностью исключить зависимость itertools:

def chain_map_accumulate(seq, f): 
    return (x + (n,) for x in seq for n in f(x[-1])) 

def accumulative_product2(*gens): 
    return reduce(chain_map_accumulate, gens[1:], (tuple(x) for x in gens[0]())) 
+1

Это причиняет боль моему мозгу, но есть ли способ использовать «itertools.accumulate» 3.3. его параметр функции для упрощения/комплектации этого? – DSM

+0

Я чувствую, что ты прав, что должен быть способ сделать эту работу. Возможно, вы сможете использовать 'accumulate' вместо' reduce' и накапливать последовательность значений 'a',' b' и 'c'; но количество значений 'a' будет отличаться от числа значений' b', которые будут отличаться от числа значений 'c'. Затем вам нужно выяснить, как перераспределить значения в плоскую последовательность корней 'a, b, c'. Задача состоит в том, чтобы сделать это элегантно ... – senderle

+0

С накоплением вы получите нечто вроде -> '('A', 'B'), ((10, 11), (11, 12)), ((' 10Z ',' 10Y '), (' 11Z ',' 11Y '), (' 11Z ',' 11Y '), (' 12Z ',' 12Y ')) 'Я не уверен, что это будет чище. – Moberg

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