Mit der neuen bereichsbasierten for-Schleife können wir Code wie schreiben
for(auto x: Y) {}
Welche IMO ist eine enorme Verbesserung von (zum Beispiel)
for(std::vector<int>::iterator x=Y.begin(); x!=Y.end(); ++x) {}
Kann es verwendet werden, um zwei gleichzeitige Schleifen wie die Pythons- zip
Funktion zu durchlaufen ? Für diejenigen, die mit Python nicht vertraut sind, gilt der Code:
Y1 = [1,2,3]
Y2 = [4,5,6,7]
for x1,x2 in zip(Y1,Y2):
print x1,x2
Gibt als Ausgabe (1,4) (2,5) (3,6)
for
kann nur mit einer Variablen verwendet werden, also nein. Wenn Sie auf zwei Werte gleichzeitig zugreifen möchten, müssen Sie Folgendes verwendenstd::pair
zip()
Funktion entwickeln, die Tupel zurückgibt und die Liste der Tupel durchläuft.for(;;)
dass dieses Verhalten, wenn auch auf lange Sicht, auftreten kann. Ist die Frage also wirklich: Ist es möglich, zwei Objekte gleichzeitig "automatisch" zu bearbeiten?Antworten:
Warnung:
boost::zip_iterator
undboost::combine
ab Boost 1.63.0 (26. Dezember 2016) wird ein undefiniertes Verhalten verursacht, wenn die Länge der Eingabecontainer nicht gleich ist (es kann über das Ende hinaus abstürzen oder iterieren).Ab Boost 1.56.0 (2014 Aug 7) können Sie Folgendes verwenden
boost::combine
(die Funktion ist in früheren Versionen vorhanden, jedoch nicht dokumentiert):Dies würde drucken
In früheren Versionen konnten Sie einen Bereich wie folgt definieren:
Die Verwendung ist die gleiche.
quelle
optional
Elementen für Iterationsmöglichkeiten hinter dem Ende in Versuchung geführt werden ...Also habe ich diesen Zip geschrieben, als ich gelangweilt war. Ich habe beschlossen, ihn zu posten, weil er sich von den anderen darin unterscheidet, dass er keinen Boost verwendet und eher wie die c ++ stdlib aussieht.
Anwendungsbeispiel:
quelle
[](int i,int j,float k,float l)
funktioniert? Ist das eine Lambda-Funktion?std::for_each
aber Sie können eine beliebige Anzahl von Bereichen verwenden. Die Parameter im Lambda hängen davon ab, wie viele Iteratoren Sie der Funktion gebenSie können eine Lösung verwenden, die auf basiert
boost::zip_iterator
. Erstellen Sie eine falsche Containerklasse, die Verweise auf Ihre Container verwaltet undzip_iterator
von den Funktionenbegin
undend
member zurückkehrt. Jetzt kannst du schreibenBeispielimplementierung (bitte testen):
Ich überlasse die Variadic-Version dem Leser als hervorragende Übung.
quelle
boost::iterator_range
+ recht einfach zu implementieren seinboost::zip_iterator
, auch in der variadischen Version.boost::zip_iterator
funktioniert nicht mit Bereichen unterschiedlicher Längestd :: transform kann dies trivial tun:
Wenn die zweite Sequenz kürzer ist, scheint meine Implementierung standardmäßig initialisierte Werte zu liefern.
quelle
b
.Suchen Sie
<redi/zip.h>
nach einerzip
Funktion, die mit der Bereichsbasis arbeitetfor
und eine beliebige Anzahl von Bereichen akzeptiert, die r- oder l-Werte sein können und unterschiedliche Längen haben können (die Iteration stoppt am Ende des kürzesten Bereichs).Druckt
0 1 2 3 4 5
quelle
boost/tuple/tuple_io.hpp
tocout << i;
boost::get<0>(i)
und verwendenboost::get<1>(i)
. Ich bin mir nicht sicher, warum das Originalbeispiel nicht direkt angepasst werden konnte. Dies hat möglicherweise damit zu tun, dass mein Code ständig auf Container verweist.Mit range-v3 :
Die Ausgabe:
quelle
Ich bin unabhängig auf dieselbe Frage gestoßen und mochte die Syntax der oben genannten Fragen nicht. Ich habe also eine kurze Header-Datei, die im Wesentlichen dasselbe tut wie der boost zip_iterator, aber einige Makros enthält, um die Syntax für mich angenehmer zu machen:
https://github.com/cshelton/zipfor
Zum Beispiel können Sie tun
Der wichtigste syntaktische Zucker ist, dass ich die Elemente aus jedem Container benennen kann. Ich füge auch eine "mapfor" hinzu, die dasselbe tut, jedoch für Maps (um die ".first" und ".second" des Elements zu benennen).
quelle
quelle
Wenn Sie eine Überladung des Bedieners mögen, haben Sie drei Möglichkeiten. Die ersten beiden werden verwendet
std::pair<>
undstd::tuple<>
sind, wie Iteratoren; Der dritte erweitert dies auf bereichsbasiertfor
. Beachten Sie, dass diese Definitionen der Operatoren nicht jedem gefallen werden. Bewahren Sie sie daher am besten in einem separaten Namespace auf und verwenden Sie eineusing namespace
in den Funktionen (keine Dateien!), In denen Sie diese verwenden möchten.quelle
Für eine C ++ - Stream-Verarbeitungsbibliothek, die ich schreibe, suchte ich nach einer Lösung, die nicht auf Bibliotheken von Drittanbietern basiert und mit einer beliebigen Anzahl von Containern arbeitet. Am Ende hatte ich diese Lösung. Es ähnelt der akzeptierten Lösung, die Boost verwendet (und führt auch zu undefiniertem Verhalten, wenn die Containerlängen nicht gleich sind).
quelle
operator*
forseq::iterator
einestd::tuple
von const-Referenzen zurückgibt .Wenn Sie einen C ++ 14-kompatiblen Compiler (z. B. gcc5) haben, können Sie ihn
zip
in dercppitertools
Bibliothek von Ryan Haining verwenden, was sehr vielversprechend aussieht:quelle
Boost.Iterators
zip_iterator
können Sie verwenden (Beispiele in den Dokumenten). Es funktioniert nicht mit Reichweite für, aber Sie könnenstd::for_each
und ein Lambda verwenden.quelle
for_each
wäre weniger mühsam.std::for_each(make_zip_iterator(make_tuple(Y1.begin(), Y2.begin())), make_zip_iterator(make_tuple(Y1.end(), Y2.end())), [](const tuple<int, int>& t){printf("%d %d\n", get<0>(t), get<1>(t)); });
?Hier ist eine einfache Version, die keinen Boost erfordert. Es ist nicht besonders effizient, da es temporäre Werte erstellt und nicht über andere Container als Listen verallgemeinert, aber keine Abhängigkeiten aufweist und den häufigsten Fall für das Zippen löst.
Obwohl die anderen Versionen flexibler sind, besteht der Sinn der Verwendung eines Listenoperators häufig darin, einen einfachen Einzeiler zu erstellen. Diese Version hat den Vorteil, dass der allgemeine Fall einfach ist.
quelle