Dzięki nowej pętli for opartej na zakresach możemy pisać kod podobny do
for(auto x: Y) {}
Która IMO jest ogromnym ulepszeniem (np.)
for(std::vector<int>::iterator x=Y.begin(); x!=Y.end(); ++x) {}
Czy można go użyć do zapętlenia dwóch równoczesnych pętli, jak zip
funkcja Pythona ? Dla osób niezaznajomionych z Pythonem kod:
Y1 = [1,2,3]
Y2 = [4,5,6,7]
for x1,x2 in zip(Y1,Y2):
print x1,x2
Daje jako wyjście (1,4) (2,5) (3,6)
for
może być używana tylko z jedną zmienną, więc nie. Jeśli chcesz uzyskać dostęp do dwóch wartości naraz, musisz użyć czegoś takiego jakstd::pair
zip()
funkcję, która zwraca krotki i iteruje listę krotek.for(;;)
jest, że można uzyskać takie zachowanie, choć na dłuższą metę, więc jest tak naprawdę pytanie: Czy jest możliwe, aby „automatycznie” nad dwoma obiektami naraz?Odpowiedzi:
Ostrzeżenie:
boost::zip_iterator
aboost::combine
od Boost 1.63.0 (26 grudnia 2016 r.) Spowoduje niezdefiniowane zachowanie, jeśli długość kontenerów wejściowych nie jest taka sama (może się zawiesić lub iterować poza końcem).Począwszy od Boost 1.56.0 (7 sierpnia 2014 r.) Możesz użyć
boost::combine
(funkcja istnieje we wcześniejszych wersjach, ale nie jest udokumentowana):To by się wydrukowało
We wcześniejszych wersjach można było samodzielnie zdefiniować zakres w następujący sposób:
Użycie jest takie samo.
źródło
optional
elementy do możliwości past-the-end iteracji ...Więc napisałem ten plik zip wcześniej, kiedy się nudziłem, zdecydowałem się go opublikować, ponieważ różni się od innych, ponieważ nie używa boost i wygląda bardziej jak stdlib c ++.
Przykładowe zastosowanie:
źródło
[](int i,int j,float k,float l)
działa? Czy to jest funkcja lambda?std::for_each
ale możesz użyć dowolnej liczby zakresów, parametry wstd :: transform może to zrobić trywialnie:
Jeśli druga sekwencja jest krótsza, moja implementacja wydaje się podawać domyślne zainicjalizowane wartości.
źródło
b
.Możesz skorzystać z rozwiązania opartego na
boost::zip_iterator
. Utwórz fałszywą klasę kontenera zachowującą odwołania do kontenerów i zwracającą sięzip_iterator
z funkcjibegin
iend
składowych. Teraz możesz pisaćPrzykładowa implementacja (prosimy o przetestowanie):
Wersję wariadyczną pozostawiam czytelnikowi jako doskonałe ćwiczenie.
źródło
boost::iterator_range
+boost::zip_iterator
, nawet w wersji wariadycznej.boost::zip_iterator
nie działa z zakresami o różnych długościachPatrz
<redi/zip.h>
nazip
funkcję, która współpracuje z zakresu bazyfor
i przyjmuje dowolną liczbę przedziałów, które mogą być rvalues lub lwartościami i może mieć różną długość (będzie iteracji zatrzymuje się na koniec krótkim zasięgu).Wydruki
0 1 2 3 4 5
źródło
boost/tuple/tuple_io.hpp
docout << i;
boost::get<0>(i)
iboost::get<1>(i)
. Nie jestem pewien, dlaczego oryginalna próbka nie mogła zostać dostosowana bezpośrednio, może to mieć związek z faktem, że mój kod stale odwołuje się do kontenerów.Z zakresem-v3 :
Wyjście:
źródło
Natknąłem się na to samo pytanie niezależnie i nie podobała mi się składnia żadnego z powyższych. Mam więc krótki plik nagłówkowy, który zasadniczo robi to samo, co przyspieszenie zip_iterator, ale ma kilka makr, aby uczynić składnię bardziej przyjemną dla mnie:
https://github.com/cshelton/zipfor
Na przykład możesz to zrobić
Głównym cukrem syntaktycznym jest to, że mogę nazwać elementy z każdego pojemnika. Dołączam również „mapfor”, które robi to samo, ale dla map (aby nazwać „.first” i „.second” elementu).
źródło
źródło
Jeśli lubisz przeciążanie operatorów, oto trzy możliwości. Pierwsze dwa używania
std::pair<>
istd::tuple<>
odpowiednio jako iteratorami; trzecia rozszerza to na podstawie zakresufor
. Zauważ, że nie każdemu spodobają się te definicje operatorów, więc najlepiej trzymać je w oddzielnej przestrzeni nazw i miećusing namespace
w funkcjach (nie plikach!), W których chciałbyś ich używać.źródło
W przypadku biblioteki przetwarzania strumieniowego C ++, którą piszę, szukałem rozwiązania, które nie opiera się na bibliotekach innych firm i działa z dowolną liczbą kontenerów. Skończyło się na tym rozwiązaniu. Jest podobny do przyjętego rozwiązania, które wykorzystuje doładowanie (a także powoduje niezdefiniowane zachowanie, jeśli długości kontenerów nie są równe)
źródło
operator*
forseq::iterator
zwraca astd::tuple
z odwołań do const.Jeśli masz kompilator zgodny z C ++ 14 (np. Gcc5), możesz użyć
zip
dostarczonego wcppitertools
bibliotece przez Ryana Haininga, który wygląda naprawdę obiecująco:źródło
Boost.Iterators ma,
zip_iterator
którego możesz użyć (przykłady w dokumentacji). Nie będzie działać z zakresem dla, ale możesz użyćstd::for_each
i lambda.źródło
for_each
byłoby mniej kłopotliwe.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)); });
?Oto prosta wersja, która nie wymaga doładowania. Nie będzie szczególnie wydajne, ponieważ tworzy wartości tymczasowe i nie generalizuje innych kontenerów niż listy, ale nie ma żadnych zależności i rozwiązuje najczęstszy przypadek kompresowania.
Chociaż inne wersje są bardziej elastyczne, często celem użycia operatora listy jest stworzenie prostego jednowierszowego. Ta wersja ma tę zaletę, że typowy przypadek jest prosty.
źródło