2010-11-08 72 views
2

這對我來說並不是一項微不足道的任務,我也找不到任何收據,所以也許你可以指給我一個,或者你有一個準備好,適當和好的爲此調整的解決方案?正確的意義對於不知道自己長度的迭代器(沒有__len__)也適用,並且可用於可耗盡的迭代器(例如鏈式迭代器);良好的調整意義很快。python itizip循環遍歷所有iterables,直到最長完成

注意:由於必須緩存迭代器輸出以重新迭代它們(Glenn Maynard指出),因此解決方案不可行。

用法示例:

>>> list(izip_cycle(range(2), range(5), range(3))) 
[(0, 0, 0), (1, 1, 1), (0, 2, 2), (1, 3, 0), (0, 4, 1)] 
>>> from itertools import islice, cycle, chain 
>>> list(islice(izip_cycle(cycle(range(1)), chain(range(1), range(2))), 6)) 
[(0, 0), (0, 0), (0, 1), (0, 0), (0, 0), (0, 1)] 

回答

1

這裏的靈感來自itertools.teeitertools.cycle。它適用於任何類型的可迭代的:

class izip_cycle(object): 
    def __init__(self, *iterables): 
     self.remains = len(iterables) 
     self.items = izip(*[self._gen(it) for it in iterables]) 

    def __iter__(self): 
     return self.items 

    def _gen(self, src): 
     q = [] 
     for item in src: 
      yield item 
      q.append(item) 

     # done with this src 
     self.remains -=1 
     # if there are any other sources then cycle this one 
     # the last souce remaining stops here and thus stops the izip 
     if self.remains: 
      while True: 
       for item in q: 
        yield item 
+0

不錯,但有一個錯誤:在重新循環迭代器之前,你可能需要額外的'if self.remains'檢查(否則你會在有限的情況下去掉一個元素)。 – trybik 2010-11-09 00:01:22

+0

@trybik是啊,我正在編輯時,你寫了:) – 2010-11-09 00:02:59

+0

只是美學:我實際上想'如果self.remains'減少'_gen'中的計數器。這樣,你甚至可以跳過'__iter__'中的這個檢查,並且有'while True:yield next(self.items)'('izip'將會停止)。幾乎沒有快速檢查更多的交易沒有產生不必要的元組+ 3行代碼更少+甚至更好的可讀性:)但是,我非常喜歡這個 - 確切地說它應該是什麼,並且非常可讀。 – trybik 2010-11-09 00:23:44

1

一個簡單的做法,可能會爲你工作,這取決於你的要求是:

import itertools 

def izip_cycle(*colls): 
    maxlen = max(len(c) if hasattr(c,'__len__') else 0 for c in colls) 
    g = itertools.izip(*[itertools.cycle(c) for c in colls]) 

    for _ in range(maxlen): 
     yield g.next() 

的第一件事,這並找到它最長序列的長度以知道要重複多少次。沒有__len__的序列被計爲長度爲0;這可能是你想要的 - 如果你有一個無止境的序列,你可能想重複有限序列。雖然這不處理沒有長度的有限迭代器。

我們絕不使用itertools.cycle來創建每個迭代器的循環版本,然後使用itertools.zip將它們壓縮在一起。

最後,我們從我們的zip文件中得到每個條目,直到我們給出了我們期望的結果數量。

如果您希望此爲有限迭代器沒有len我們需要做更多的工作自己的工作:

def izip_cycle(*colls): 
    iters = [iter(c) for c in colls] 
    count = len(colls) 
    saved = [[] for i in range(count)] 
    exhausted = [False] * count 

    while True: 
     r = [] 

     for i in range(count): 
      if not exhausted[i]: 
       try: 
        n = iters[i].next() 
        saved[i].append(n) 
        r.append(n) 
       except StopIteration: 
        exhausted[i] = True 
        if all(exhausted): 
         return 
        saved[i] = itertools.cycle(saved[i]) 
      if exhausted[i]: 
       r.append(saved[i].next()) 

     yield r 

這基本上是Python implementation of itertools.cycle in the documentation的擴展運行在多個序列。我們將我們在saved中看到的項目放在一起,以重複並追蹤哪些序列在exhausted中耗盡。

由於此版本等待所有序列耗盡,如果您傳入某些無限循環,循環將永遠運行。

+0

如果你知道'maxlen'爲什麼不使用'for'循環? – SilentGhost 2010-11-08 16:56:13

+0

如果最長的序列沒有長度,就會中斷:'izip_cycle(iter([1,2,3,4]),[1,2]))' – mikerobi 2010-11-08 17:00:11

+0

@SilentGhost - for循環可能更好。已經改變了代碼。 – 2010-11-08 17:05:33

0
def izip_cycle_inplace(*iterables): 
    def wrap(it): 
     empty = True 
     for x in it: empty = yield x 
     if empty: return 
     next(counter) 
     while True: 
      empty = True 
      for x in it: empty = yield x 
      if empty: raise ValueError('cannot cycle iterator in-place') 
    iterators = [wrap(i) for i in iterables] 
    counter = iter(iterators) 
    next(counter) 
    while True: 
     yield [next(i) for i in iterators] 

def izip_cycle(*iterables): 
    def wrap(it): 
     elements = [] 
     for x in it: 
      yield x 
      elements.append(x) 
     if not elements: return 
     next(counter) 
     while True: 
      for x in elements: yield x 
    iterators = [wrap(i) for i in iterables] 
    counter = iter(iterators) 
    next(counter) 
    while True: 
     yield [next(i) for i in iterators] 
+0

這些都很光滑,但會因無法使用迭代器而陷入無限循環。嘗試,例如'list(itertools.islice(izip_cycle(itertools.cycle(range(1)),itertools.chain(range(1),range(1)))3))' – trybik 2010-11-08 22:17:17

+0

那麼,你問了一個原地解決方案。 ;)雖然這些也陷入了空的迭代。如果兩個for循環中的任何一個都完成而沒有產生任何您想要引發ValueError的任何事情,但我看不到一個優雅的方式來添加... – adw 2010-11-09 07:25:01

+0

好吧,現在解決所有問題,我想。 – adw 2010-11-09 17:09:13