2012-04-05 7 views
5

Erstens prüfe ich die C++ Stil Iteratoren quickly.for Beispiel:Wie macht man expressivere Python-Iteratoren? wie C++ Iterator

//--- Iterating over vector with iterator. 
vector<int> v; 
. . . 
for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) { 
    cout << *it << endl; 
} 

Es flexibel ist. Es ist leicht, zugrunde liegende Containertypen zu ändern. Beispielsweise könnten Sie später entscheiden, dass die Anzahl der Einfügungen und Löschungen so hoch ist, dass eine Liste effizienter ist als ein Vektor. Es hat auch viele nützliche Elementfunktionen. Viele der Elementfunktionen für Vektor verwenden Iteratoren, z. B. zuweisen, einfügen oder löschen. Darüber hinaus können wir einen Iterator (falls unterstützt) bidirektional verwenden, z. B. ++, -. Dies ist nützlich, um einen Stream wie Objekte zu analysieren.

Die Probleme von Python ist: 1: Derzeit ist Python für Schleifensyntax weniger flexibel als C++ für. (naja, sicherer) 2: statt "it! = iter.end()" style, wird python eine Ausnahme auslösen, wenn next() nicht mehr hat. Es ist nicht flexibel.

Frage 1: Ist meine obige Idee richtig?

OK. Hier kommt meine Frage, wie man einen leistungsfähigeren Python-Iterator so leistungsfähig wie C++ - Iteratoren implementiert? Derzeit ist die Syntax für Python for-Schleifen weniger flexibel als für C++. Ich finde auch einige mögliche Lösungen, wie http://www.velocityreviews.com/forums/t684406-pushback-iterator.html. aber es fragt Benutzer push_back ein Zeug statt Iterator zu fragen -.

Frage 2: Was ist der beste Weg, um einen bidirektionalen Iterator in Python zu implementieren? Genau wie http://www.cplusplus.com/reference/std/iterator/BidirectionalIterator/. Der Pseudo-Code ist der folgende:

it = v.begin(); 
while(it!=v.end()) { 
    //do sth here 

    if (condition1) 
     ++it;//suppose this iterator supports ++ 
    if(condition2) 
     --it;//suppose this iterator supports -- 
} 

Die wichtigsten Eigenschaften sind: 1) bidirektional, 2) einfacher "Ende" zu überprüfen. Die "++" oder "-" Operatoren oder allgemeinen Funktionen sind nicht wichtig (es hat sowieso keinen semantischen Unterschied).

Danke,

Update: ich aus den Antworten einige mögliche Lösungen bekam:

i = 0 
while i < len(sequence): # or i < len and some_other_condition 
    star_it = sequence[i] 
    if condition_one(star_it): 
     i += 1 
    if condition_two(star_it): 
     i = max(i - 1, 0) 

Doch anders als Array sollte willkürlichen Zugriff auf die Liste O (n) sein. Ich nehme an, dass das "list" -Objekt in Python intern unter Verwendung von Linked-List-ähnlichen Dingen implementiert ist. Daher ist diese While-Loop-Lösung nicht effizient. In C++ haben wir jedoch "zufälliger Iterator", "bidirektionaler Iterator". Wie soll ich eine bessere Lösung bekommen? Vielen Dank.

+1

Können Sie ein konkretes Beispiel für etwas geben, was Sie in 'Python' nicht tun können, was Sie einfach in' C++ 'tun können? –

+0

Python 'Ausbeute' + Ausnahme Mechanismus ist erstaunlich flexibel (es ist, was wir" Fortsetzungen "in der funktionalen Programmierung nennen). Lerne sie richtig zu benutzen und du wirst belohnt werden. Es ist viel einfacher, zusammengesetzte Iteratoren in Python als in C++ zu definieren. –

+0

Diese Frage ist zu vage und es gibt keine echte Antwort auf die Frage in der aktuellen Form. –

Antwort

0

Eigentlich ist C++ Iterator-System nicht so toll. Iteratoren ist auf Zeiger verwendet, und sie haben ihre Leiden:

  • singuläre Werte: v.end() nicht sicher
  • Inversionsprobleme dereferenziert werden: std::for_each(end, begin, func);
  • Mismatch Fragen: std::for_each(v0.begin(), v2.end(), func);

Python Ansatz viel ist besser in dieser Hinsicht (obwohl die Verwendung von Ausnahme zunächst ziemlich überraschend sein kann, hilft es wirklich, verschachtelte Iteratoren zu definieren), weil ein Python-Iterator im Gegensatz zu seinem Namen eher mit einemverwandt ist 210.

Das Konzept der Range ist so viel besser als 11 C++ stellt die Bereich-for-Schleife-Konstrukt:

for (Object& o: range) { 
} 

Alles, was mit einem Iterator möglich ist, mit einem Bereich möglich ist, obwohl es dauern einige Male, um es zu realisieren und einige Übersetzungen scheinen surrealists zuerst für diejenigen von uns, die mit C++ Zeiger-ähnlichen Iteratoren erzogen wurden. Zum Beispiel können Teilbereiche perfekt ausgedrückt werden:

for (Object& o: slice(range, 2, 9)) { 
} 

wo slice alle Elemente in Position [2, 9) innerhalb range nehmen würde.

Also, anstatt Ihre Sprache (Python) zu bekämpfen, sollten Sie tiefer hineingehen und seinen Stil annehmen. Der Kampf gegen eine Sprache ist im Allgemeinen eine verlorene Schlacht, lernen ihre Idiome, werden effizient.

+0

Einfach gesagt, ich brauche nur einen bidirektionalen Iterator. Sei es, wir haben so viel Grammatik Zucker, es sieht einfach aus, aber nicht so dumm und transparent in C++. –

5

Für die meisten Situationen sind Pythons for und Iteratoren die einfachsten Dinge. Das ist ihr Ziel und sie sollten es nicht für die Flexibilität kompromittieren - ihre mangelnde Flexibilität ist kein Problem.

Für einige Situationen, in denen Sie keine for-Schleife verwenden konnten, könnten C++ - Iteratoren einfacher sein. Aber es gibt immer einen Weg, dies in Python zu tun, der nicht komplexer ist als ein C++ - Iterator.


Wenn Sie den Iterator vorrückenden aus Looping, nur verwenden, um eine while Schleife trennen müssen:

it = iter(obj) 

try: 
    while True: # or some secondary break condition other than StopIteration 
     star_it = next(it) 
     if condition_one(star_it): 
      star_it = next(it) 
except StopIteration: 
    pass # exhausted the iterator 

ich denken kann, nur zwei Situationen, in denen --it Sinn in Python machen.

Das erste ist, dass Sie über eine Sequenz iterieren. In diesem Fall, wenn Sie rückwärts gehen müssen, nicht einen Iterator verwenden überhaupt - nur einen Zähler mit einem while Schleife verwenden:

i = 0 
while i < len(sequence): # or i < len and some_other_condition 
    star_it = sequence[i] 
    if condition_one(star_it): 
     i += 1 
    if condition_two(star_it): 
     i = max(i - 1, 0) 

Die zweite ist, wenn Sie über eine doppelt verknüpfte Liste sind Iterieren . In diesem Fall wieder, nicht einen Iterator verwenden - nur die Knoten durchqueren normalerweise:

current = node 
while current: # or any break condition 
    if condition_one(current): 
     current = current.next 
    if condition_two(star_it): 
     current = current.prev 

Eine Situation, wo Sie könnte denke, es macht Sinn, aber man kann nicht eine der beiden oben genannten Methoden verwenden , ist mit einer ungeordneten Sammlung wie set oder dict. Allerdings macht --itnicht Sinn in diesem Fall. Da die Sammlung ungeordnet ist, wäre semantisch jedes der zuvor erreichten Elemente geeignet - nicht nur das tatsächliche vorherige Element.

Also, um das richtige Objekt zu wissen, um zu gehen zurück, du Speicher benötigen, entweder durch wie mydict.values() oder tuple(myset) über eine Sequenz laufen und unter Verwendung eines Zählers oder durch eine Folge von früheren Werten der Montage, wie Sie gehen und mit a while Schleife und next wie oben anstelle einer for Schleife.

+1

@XinlinCao Aber Lösungen, die auf diese Situationen zugeschnitten sind, sind einfacher zu benutzen als eine generische, besonders wenn die generische für andere Situationen komplexer gestaltet werden müsste. Python hat Tools zum Parsen. Was Dateien angeht, sehe ich nicht, wie ein Iterator besser ist, als nur 'seek' aufzurufen? – agf

+2

@XinlinCao: Ihre Anwendungsfälle sind viel zu vage. Geben Sie einen konkreten realen Anwendungsfall und wir können Ihnen eine gute Lösung in Python zeigen. –

+0

@XinlinCao Wir versuchen dir zu sagen, __das ist die falsche Frage__. Es gibt bessere Möglichkeiten in Python zu tun, was Sie mit einem bidirektionalen Iterator in C++ tun würden. – agf

0

Sie könnten eine ähnliche Art und Weise von C++ mit Python-Objekte implementieren:

class Iterable(object): 
    class Iterator(object): 
    def __init__(self, father, pos=0): 
     self.father = father 
     self.pos = pos 

    def __getitem__(self, pos=0): 
     return self.father[self.pos + pos] 

    def __setitem__(self, pos, value): 
     self.father[self.pos + pos] = value 

    def __iadd__(self, increment): 
     self.pos += increment 
     return self 

    def __isub__(self, decrement): 
     self.pos -= decrement 
     return self 

    def __ne__(self, other): 
     return self.father != other.father or self.pos != other.pos 

    def __eq__(self, other): 
     return not (self != other) 

    def begin(self): 
    return self.Iterator(self) 

    def end(self): 
    return self.Iterator(self, len(self)) 

class Vector(list, Iterable): 
    pass 

v = Vector([54, 43, 32, 21]) 

counter = 0 
it = v.begin() 
print it, it[0] 
while it != v.end(): 
    counter += 1 
    print it[0] 
    if counter == 2: 
    it += 1; # suppose this iterator supports ++ 
    if counter == 1: 
    it -= 1; # suppose this iterator supports -- 
    it += 1 

Dies ersetzt *it durch it[0] (auch C-Analog ++) und it++ von it += 1, aber es bleibt so ziemlich das gleiche in Wirkung.

Sie verlassen die Pythonic Möglichkeiten, wenn Sie dies tun, obwohl ;-)

1

Lösungen für einige Situationen, die Sie erwähnt:

  1. Sie Objekte in der darunterliegenden Behälter ersetzen. Für Wörterbücher, iterieren über die Tasten oder Gegenstände, nicht nur die Werte:

    for key, value in my_dict.iteritems(): 
        if conditiion(value): 
         my_dict[key] = new_value 
    

    für Listen enumerate() verwenden:

    for index, item in enumerate(my_list): 
        if condition(item): 
         my_list[index] = new_item 
    
  2. Sie wollen einen Iterator mit einer „Vorausschau“ -Wert. Sie würden wahrscheinlich etwas zugeschnitten auf eine bestimmte Situation verwenden, aber hier ist ein Rezept für allgemeine Situationen:

    def iter_with look_ahead(iterable, sentinel=None): 
        iterable, it_ahead = itertools.tee(iterable) 
        next(it_ahead, None) 
        return izip_longest(iterable, it_ahead, fillvalue=sentinel) 
    
    for current, look_ahead in iter_with look_ahead(tokens): 
        # whatever 
    
  3. Sie wollen in umgekehrter Richtung zu durchlaufen. Verwenden Sie reversed() für Container, die es unterstützen.

  4. Sie möchten einen wahlfreien Zugriff. Schalten Sie einfach Ihre iterable in eine Liste und Verwendung Indizes:

    my_list = list(my_iterable) 
    
0

Beachten Sie, dass das List-Objekt in Python ein Array ist, so dass die Effizienz Sorge in der Frage erwähnt ist eigentlich kein Thema.

+1

Dies sollte im Kommentarabschnitt statt Lösung sein. – DaveyLaser