2010-12-10 90 views
3

我想更多地使用循環迭代器,因爲我聽說它比索引循環更快。我不確定的一件事是如何很好地處理序列的結尾。我能想到的方式是使用tryexcept StopIteration,這對我來說看起來很醜。如何優雅地使用Python迭代器

更具體地說,假設我們被要求打印兩個排序列表ab的合併排序列表。我會寫下面的內容

aNull = False 
I = iter(a) 
try: 
    tmp = I.next() 
except StopIteration: 
    aNull = True 

for x in b: 
    if aNull: 
     print x 
    else: 
     if x < tmp: 
      print x 
     else: 
      print tmp,x 
      try: 
       tmp = I.next() 
      except StopIteration: 
       aNull = True 

while not aNull: 
    print tmp 
    try: 
     tmp = I.next() 
    except StopIteration: 
     aNull = True 

你會怎麼編碼使它更整潔?

+0

b是什麼意思? – detly 2010-12-10 09:08:40

+2

該代碼幾乎難以辨認。描述它應該做什麼。 – 2010-12-10 09:09:53

+0

a和b是兩個排序列表。任務是以非遞減的順序打印這兩個列表的元素 – nos 2010-12-10 09:25:34

回答

7

我認爲更對稱地處理ab會使讀起來更容易。此外,使用在Python 2.6內置next函數的默認值避免了需要處理StopIteration

def merge(a, b): 
    """Merges two iterators a and b, returning a single iterator that yields 
    the elements of a and b in non-decreasing order. a and b are assumed to each 
    yield their elements in non-decreasing order.""" 

    done = object() 
    aNext = next(a, done) 
    bNext = next(b, done) 

    while (aNext is not done) or (bNext is not done): 
     if (bNext is done) or ((aNext is not done) and (aNext < bNext)): 
      yield aNext 
      aNext = next(a, done) 
     else: 
      yield bNext 
      bNext = next(b, done) 

for i in merge(iter(a), iter(b)): 
    print i 

以下功能概括爲任意多的迭代器的工作方式。

def merge(*iterators): 
    """Merges a collection of iterators, returning a single iterator that yields 
    the elements of the original iterators in non-decreasing order. Each of 
    the original iterators is assumed to yield its elements in non-decreasing 
    order.""" 

    done = object() 
    n = [next(it, done) for it in iterators] 

    while any(v is not done for v in n): 
     v, i = min((v, i) for (i, v) in enumerate(n) if v is not done) 
     yield v 
     n[i] = next(iterators[i], done) 
+1

當然,如果你真的想合併兩個列表,你應該使用標準庫函數'heapq.merge'。 – jchl 2010-12-10 09:37:43

+0

如果以發生器的形式完成,這會更好 - 將a和b傳入並將'print'語句替換爲yield。然後你可以做任何你想要的結果,它會成爲一個迭代器。 – neil 2010-12-10 11:41:41

+0

@neil同意。我想到了這一點,但並不認爲這個例子中額外的複雜性是值得的。但既然你也提到過,我想我會按照你的建議重寫它。 – jchl 2010-12-10 11:48:14

5

你錯過了迭代器的全部觀點。您不需要手動撥打I.next(),只需通過I進行重複。

for tmp in I: 
    print tmp 

編輯

要合併兩個迭代器,該itertools模塊中使用非常方便的功能。你想要的是可能izip

merged = [] 
for x, y in itertools.izip(a, b): 
    if x < y: 
     merged.append(x) 
     merged.append(y) 
    else: 
     merged.append(y) 
     merged.append(x) 

再次編輯

正如在評論中指出,這實際上不會工作,因爲有可能是從列表比下一個更小的多個項目列表b中的項目。但是,我意識到還有另一個內置函數可以處理這個問題:heapq.merge

+1

我不明白如何使用'for'合併兩個迭代器。 – jchl 2010-12-10 09:37:05

+0

這是行不通的 - 一個迭代器之間可能有多個項目在兩個之間。 – neil 2010-12-10 11:29:47

+0

@neil是的,我剛剛意識到這一點。必須考慮更多。 – 2010-12-10 11:31:52

0

函數sorted適用於列表和迭代器。也許這不是你想要的,但下面的代碼有效。

 

a.expand(b) 
print sorted(iter(a)) 
 
+0

排序轉換iter(a)到列表中,然後對其進行排序,因此您不使用生成器... – Ant 2010-12-10 09:54:30

+0

很高興知道,謝謝 – jaume 2010-12-10 10:06:49