2013-02-23 48 views
11

Python的succint语法通过其电池允许冗长的代码行以可读的一行表示。请看下面的例子带有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,          | 
====================================================| 

后期我结束了一个深嵌套依赖环路我试图简洁表达,但未能

循环的结构将是如下

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 2013-02-23 04:56:13

+0

你在最里面的循环体中使用'a','b'(中间值)吗? – jfs 2013-02-23 04:57:15

+0

@liori:Prolog总是引诱我,但从来没有聪明到学习人工智能 – Abhijit 2013-02-23 04:57:49

回答

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

几乎所有的复杂性来自于投入的积累,如上述代码展示了一个快速的“推导”。所述最终可使用只是几个嵌套itertools构建体的生成(c)值:

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

这可以用reduce一概而论。要使用reducechain_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'] 

的最终任务是积累的初始值,让你有机会获得abc。这需要一点思想得到正确,但执行是相当简单 - 我们只需要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)) 

这要求首先输入被包裹在元组,所以我们映射Atuple

>>> 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可以使用genex更简洁一点。这可以与一个简短的定义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

这会伤害我的大脑,但有没有办法使用3.3的'itertools.accumulate'与其功能参数简化/复杂化? – DSM 2013-02-23 16:40:29

+0

我觉得你是对的,必须有一种方法来完成这项工作。您可以使用'accumulate'而不是'reduce',并累加一系列'a','b'和'c'值;但“a”值的数量与“b”值的数量不同,后者会与“c”值的数量不同。然后你必须弄清楚如何将这些值重新分配到一个“a,b,c”元组的平面序列中。挑战将是优雅地做... – senderle 2013-02-23 17:33:38

+0

从积累你会得到像 - >'('A','B'),((10,11),(11,12)),((' 10Z','10Y'),('11Z','11Y'),('11Z','11Y'),('12Z','12Y'))''我不确定这是否是更清洁的。 – Moberg 2017-01-28 09:00:17