2016-05-23 2 views
1

Ich weiß, dass ich einen Iterator kopierenWie umleiten Daten in einem Iterator in zwei andere?

x1, x2 = itertools.tee(x)

Dann wird, um zwei Generatoren zu bekommen verwenden, könnte ich filtern:

filter(..., x1); filter(..., x2)

aber dann würde ich das gleiche läuft Berechnung zweimal, dh gehe durch x in x1 und x2.

So würde ich etwas effizienter wie das tun:

x1, x2 = divert(into x1 if ... else x2, x)

mag dies alles in Python existieren 3?

+0

Auf die Gefahr etwas Dummes zu sagen - wollen/müssen sie in Generatoren bleiben? Sie könnten die beiden Listen einfach in einer normalen alten 'for'-Schleife erstellen; aber ich schätze, du willst 'x' nicht unnötig erweitern? – dwanderson

+0

Kein Kommentar ist Dump, wenn es die Frage klärt :-) Ja, ich möchte den Generator-Ansatz beibehalten, um mein Gedächtnis so viel wie möglich zu befreien. – Xiphias

Antwort

0

Es gibt kein integriertes Werkzeug in Python geschrieben, die ich kenne. Es ist ein kleiner Trick, um zu funktionieren, da es keine Garantie für die Aufrufreihenfolge jedes Iterators gibt, den Sie erzeugen können.

Zum Beispiel x könnte einen x1 Wert von einem x2 Wert gefolgt produzieren, aber Ihr Code könnte über x1 laufen, bis er einen Signalwert erzeugt, dann auf x2 laufen, bis er einen Signalwert erzeugt ... Also im Grunde würde der Code müssen alle x2 Werte halten, bis ein x1 Wert generiert wird, der beliebig spät sein kann.

Wenn das wirklich das ist, was Sie tun möchten, hier ist eine kurze Idee, wie man diesen Puffer macht. Warnung, es ist überhaupt nicht getestet und angenommen, x ist ein endloser Generator. Außerdem müssen Sie zwei tatsächliche Iterator-Klassen codieren, die __next__ implementieren, das sich auf diesen allgemeinen Iterator bezieht, einen mit category==True und den anderen mit category==False.

class SeparatedIterator: 
    def __init__(self, iterator, filter): 
     self.it = iterator 
     self.f = filter 
     #The buffer contains pairs of (value,filterIsTrue) 
     self.valueBuffer = [] 

    def generate(): 
     value = next(self.it) 
     filtered = self.f(value) 
     self.valueBuffer.append((value, filtered)) 

    def nextValue(category): 
     #search in stored values 
     for i in range(len(self.valueBuffer)): 
      value, filtered = self.valueBuffer[i] 
      if filtered == category: 
       del self.valueBuffer[i] 
       return value 

     #else, if none of the category found, 
     #generate until one of the category is made 
     self.generate() 
     while self.valueBuffer[-1][1] != category: 
      self.generate() 

     #pop the value and return it 
     value, _ = self.valueBuffer.pop() 
     return value 

Else wenn Sie mehr Kontrolle über den Iterator Anruf Auftrag haben, müssen Sie dieses Wissen nutzen, um eine maßgeschneiderte und optimierte Art und Weise zu implementieren zwischen Iteratoren Werten zu wechseln.

+0

Danke für die Idee - ich könnte es auf eine ähnliche, aber andere Weise tun. Wie auch immer, ich bin überrascht, dass es zu diesem nicht so seltenen Fall keine vorgefertigte Lösung gibt. – Xiphias

+0

Das Problem ist nicht, dass es selten ist, auch wenn es nicht üblich ist, das Problem ist, dass es keine offensichtliche Lösung gibt. Meine obige Lösung verwendet eine Liste, um die Werte zu speichern und kann eine schreckliche Leistung haben. Das kann in der Zeitleistung schrecklich sein, da es den vollen Puffer bei jeder neuen Wertanforderung iteriert. Die Verwendung von zwei Warteschlangen für jede Kategorie sollte dieses Problem mildern, aber nicht das Problem, dass die Kategorie "bis zur ersten Kategorie" gefunden wurde. –