2013-02-23 60 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