2010-12-10 13 views
3

Ich versuche, Iteratoren mehr für das Schleifen zu verwenden, da ich hörte, dass es schneller ist als Index-Schleifen. Eine Sache, über die ich mir nicht sicher bin, ist, wie man das Ende der Sequenz gut behandelt. Die Art, wie ich mir vorstellen kann, ist die Verwendung von try und except StopIteration, die für mich hässlich aussieht.Wie elegant ich Python-Iteratoren verwenden kann

Um konkreter zu werden, nehmen wir an, wir werden gebeten, die zusammengeführte sortierte Liste der zwei sortierten Listen a und b zu drucken. Ich würde folgendes schreiben:

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 

Wie würden Sie es kodieren, um es sauberer zu machen?

+0

Was ist 'b' gemeint? – detly

+2

Dieser Code ist fast unleserlich. Beschreibe, was es tun soll. –

+0

a und b sind zwei sortierte Listen. Die Aufgabe besteht darin, die Elemente dieser beiden Listen in nicht abnehmender Reihenfolge zu drucken. – nos

Antwort

7

Ich denke, Handhabung a und b mehr symmetrisch würde es leichter zu lesen. Auch mit der eingebauten in next Funktion in Python 2.6 mit einem Standardwert vermeidet die Notwendigkeit zu handhaben 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 

Die folgende Funktion verallgemeinert den Ansatz für beliebig viele Iteratoren zu arbeiten.

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

Wenn Sie tatsächlich zwei Listen zusammenführen möchten, sollten Sie natürlich die Standardbibliotheksfunktion 'heapq.merge' verwenden. – jchl

+0

Dies wäre sogar noch besser, wenn man es als Generator macht - pass a und b hinein und ersetze die "print" -Anweisungen durch "yield". Dann könnten Sie mit dem Ergebnis tun, was Sie wollen, und es wäre immer noch ein Iterator. – neil

+0

@neil Vereinbarte. Ich dachte darüber nach, dachte aber nicht, dass es die zusätzliche Komplexität für dieses Beispiel wert wäre. Aber da du es auch erwähnt hast, denke ich, werde ich es umschreiben, wie du es vorschlägst. – jchl

5

Sie vermissen den ganzen Punkt der Iteratoren. Sie rufen I.next() nicht manuell auf, Sie durchlaufen nur I.

for tmp in I: 
    print tmp 

Edited

zwei Iteratoren zu verschmelzen, verwenden Sie die sehr praktische Funktionen im itertools Modul. Diejenige, die Sie wollen, ist wahrscheinlich 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) 

Bearbeiten wieder

Wie in den Kommentaren darauf hingewiesen, das wird nicht wirklich funktionieren, weil es mehrere Elemente aus der Liste ein kleiner ist als der nächste sein könnte Artikel in Liste b. Ich erkannte jedoch, dass es eine andere eingebaute Funktion gibt, die sich damit befasst: heapq.merge.

+1

Ich sehe nicht, wie es möglich ist, zwei Iteratoren mit 'for' zusammenzuführen. – jchl

+0

Das wird nicht funktionieren - es könnte mehrere Elemente von einem Iterator zwischen zwei von dem anderen sein. – neil

+0

@neil ja, das habe ich gerade gemerkt. Muss noch etwas nachdenken. –

0

Die Funktion sorted arbeitet mit Listen und Iteratoren. Vielleicht ist es nicht das, was Sie wünschen, aber der folgende Code funktioniert.

 

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

sortiert transform iter (a) in eine Liste und sortiert sie dann, so dass Sie keine Generatoren verwenden ... – Ant

+0

Gut zu wissen, danke – jaume

Verwandte Themen