Czy można iterować wektor od końca do początku?
for (vector<my_class>::iterator i = my_vector.end();
i != my_vector.begin(); /* ?! */ ) {
}
Czy jest to możliwe tylko w przypadku czegoś takiego:
for (int i = my_vector.size() - 1; i >= 0; --i) {
}
Odpowiedzi:
Najlepszy sposób to:
for (vector<my_class>::reverse_iterator i = my_vector.rbegin(); i != my_vector.rend(); ++i ) { }
rbegin()
/rend()
zostały specjalnie zaprojektowane do tego celu. (I tak, zwiększenie wartości areverse_interator
przesuwa ją wstecz).Teoretycznie twoja metoda (przy użyciu
begin()
/end()
&--i
) zadziała,std::vector
ponieważ iterator jest dwukierunkowy, ale pamiętaj, żeend()
nie jest ostatnim elementem - jest poza ostatnim elementem, więc musisz najpierw zmniejszyć, a jesteś gotowe, gdy dotrzeszbegin()
- ale nadal musisz wykonać przetwarzanie.vector<my_class>::iterator i = my_vector.end(); while (i != my_vector.begin()) { --i; /*do stuff */ }
AKTUALIZACJA: Najwyraźniej zbyt agresywnie przepisałem
for()
pętlę wwhile()
pętlę. (Ważne jest to, że--i
jest na początku).źródło
--i
spowoduje to duży problem, jeśli pojemnik jest pusty ... Przed przejściem dodo - while
pętli warto sprawdzić(my_vector.begin() != my_vector.end())
.do-while
pętli zamiast tylkowhile
pętli? Wtedy nie potrzebowałbyś żadnego specjalnego sprawdzenia pustych wektorów.auto
dla lepszej czytelności?Jeśli masz C ++ 11, możesz skorzystać z
auto
.for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it) { }
źródło
Ugruntowany „wzorzec” odwrotnej iteracji poprzez zakresy zamknięte-otwarte wygląda następująco
// Iterate over [begin, end) range in reverse for (iterator = end; iterator-- != begin; ) { // Process `*iterator` }
lub, jeśli wolisz,
// Iterate over [begin, end) range in reverse for (iterator = end; iterator != begin; ) { --iterator; // Process `*iterator` }
Ten wzorzec jest przydatny na przykład do odwrotnego indeksowania tablicy przy użyciu indeksu bez znaku
int array[N]; ... // Iterate over [0, N) range in reverse for (unsigned i = N; i-- != 0; ) { array[i]; // <- process it }
(Ludzie, którzy nie znają tego wzorca często nalegać na użyciu podpisane całkowitą typów dla indeksowania tablicy specjalnie ponieważ błędnie uważają, że niepodpisane typy są jakoś „bezużyteczny” na odwrotnej indeksowania)
Może być używany do iteracji po tablicy przy użyciu techniki „przesuwnego wskaźnika”
// Iterate over [array, array + N) range in reverse for (int *p = array + N; p-- != array; ) { *p; // <- process it }
lub może być używany do odwrotnej iteracji po wektorze przy użyciu zwykłego (nie odwrotnego) iteratora
for (vector<my_class>::iterator i = my_vector.end(); i-- != my_vector.begin(); ) { *i; // <- process it }
źródło
--end()
end()
. Mimo że wydają się zaczynać odend()
, zawsze upewniają się, że iterator jest dekrementowany przed pierwszym dostępem.auto a = vector<int>{0,1,2}; bool reversed = 0; auto it = (!reversed?a.begin():a.end()); auto end = (reversed?a.begin():a.end());
while(it != end) { if(reversed)--it; cout << *it << endl; if(!reversed)++it; }
reversed
cztery razy - dwa z nich w pętli. Oczywiście testowanie wartości logicznych jest bardzo szybkie, ale nadal, dlaczego nie musisz pracować? Zwłaszcza, że jedynym celem wydaje się uczynienie kodu nieczytelnym. co powiesz na to, że użyjemy dwóch oddzielnych pętli?if (reversed) for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it) {doStuff(*it);} else for (auto it = my_vector.begin(); it != my_vector.end(); ++it) {doStuff(*it);}
if
ale chciałem pozbyć się szablonu nadoStuff()
. Nadal jest to wykonalne z dwoma,if
które masz, wykonując pętlę w drugą stronę na pierwszym.Począwszy od C ++ 20, możesz użyć
std::ranges::reverse_view
pętli for opartej na zakresach:#include<ranges> #include<vector> #include<iostream> using namespace std::ranges; std::vector<int> const vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for(auto& i : views::reverse(vec)) { std::cout << i << ","; }
Lub nawet
for(auto& i : vec | views::reverse)
Niestety, w chwili pisania tego tekstu (styczeń 2020 r.) Żaden główny kompilator nie implementuje biblioteki zakresów, ale możesz skorzystać z zakresów Erica Nieblera-v3 :
#include <iostream> #include <vector> #include "range/v3/all.hpp" int main() { using namespace ranges; std::vector<int> const vec{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; for(auto& i : views::reverse(vec)) { std::cout << i << ","; } return 0; }
źródło
rend() / rbegin()
Iteratory użytkownika :for (vector<myclass>::reverse_iterator it = myvector.rbegin(); it != myvector.rend(); it++)
źródło
template<class It> std::reverse_iterator<It> reversed( It it ) { return std::reverse_iterator<It>(std::forward<It>(it)); }
Następnie:
for( auto rit = reversed(data.end()); rit != reversed(data.begin()); ++rit ) { std::cout << *rit;
Alternatywnie w C ++ 14 po prostu zrób:
for( auto rit = std::rbegin(data); rit != std::rend(data); ++rit ) { std::cout << *rit;
W C ++ 03/11 większość standardowe kontenery mają
.rbegin()
i.rend()
metody, jak również.Na koniec możesz napisać adapter zakresu
backwards
w następujący sposób:namespace adl_aux { using std::begin; using std::end; template<class C> decltype( begin( std::declval<C>() ) ) adl_begin( C&& c ) { return begin(std::forward<C>(c)); } template<class C> decltype( end( std::declval<C>() ) ) adl_end( C&& c ) { return end(std::forward<C>(c)); } } template<class It> struct simple_range { It b_, e_; simple_range():b_(),e_(){} It begin() const { return b_; } It end() const { return e_; } simple_range( It b, It e ):b_(b), e_(e) {} template<class OtherRange> simple_range( OtherRange&& o ): simple_range(adl_aux::adl_begin(o), adl_aux::adl_end(o)) {} // explicit defaults: simple_range( simple_range const& o ) = default; simple_range( simple_range && o ) = default; simple_range& operator=( simple_range const& o ) = default; simple_range& operator=( simple_range && o ) = default; }; template<class C> simple_range< decltype( reversed( adl_aux::adl_begin( std::declval<C&>() ) ) ) > backwards( C&& c ) { return { reversed( adl_aux::adl_end(c) ), reversed( adl_aux::adl_begin(c) ) }; }
a teraz możesz to zrobić:
for (auto&& x : backwards(ctnr)) std::cout << x;
co myślę, że jest całkiem ładne.
źródło
Użyj odwrotnych iteratorów i pętli od
rbegin()
dorend()
źródło
Podoba mi się iterator wstecz na końcu Yakk - odpowiedź Adama Nevraumonta, ale wydawało mi się, że jest to skomplikowane dla tego, czego potrzebowałem, więc napisałem to:
template <class T> class backwards { T& _obj; public: backwards(T &obj) : _obj(obj) {} auto begin() {return _obj.rbegin();} auto end() {return _obj.rend();} };
Jestem w stanie wziąć normalny iterator, taki jak ten:
for (auto &elem : vec) { // ... my useful code }
i zmień to na to, aby iterować w odwrotnej kolejności:
for (auto &elem : backwards(vec)) { // ... my useful code }
źródło
Oto super prosta implementacja, która pozwala na użycie dla każdej konstrukcji i opiera się tylko na bibliotece standardowej C ++ 14:
namespace Details { // simple storage of a begin and end iterator template<class T> struct iterator_range { T beginning, ending; iterator_range(T beginning, T ending) : beginning(beginning), ending(ending) {} T begin() const { return beginning; } T end() const { return ending; } }; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // usage: // for (auto e : backwards(collection)) template<class T> auto backwards(T & collection) { using namespace std; return Details::iterator_range(rbegin(collection), rend(collection)); }
Działa to z rzeczami, które dostarczają rbegin () i rend (), a także z tablicami statycznymi.
std::vector<int> collection{ 5, 9, 15, 22 }; for (auto e : backwards(collection)) ; long values[] = { 3, 6, 9, 12 }; for (auto e : backwards(values)) ;
źródło
Jeśli możesz korzystać z The Boost Library, istnieje Boost.Range, który zapewnia
reverse
adapter zasięgu obejmujący:#include <boost/range/adaptor/reversed.hpp>
Następnie, w połączeniu z pętlą zakresu C ++ 11
for
, możesz po prostu napisać:for (auto& elem: boost::adaptors::reverse(my_vector)) { // ... }
Ponieważ ten kod jest krótszy niż ten wykorzystujący parę iteratorów, może być bardziej czytelny i mniej podatny na błędy, ponieważ jest mniej szczegółów, na które należy zwrócić uwagę.
źródło
boost::adaptors::reverse
jest bardzo przydatne!użyj tego kodu
//print the vector element in reverse order by normal iterator. cout <<"print the vector element in reverse order by normal iterator." <<endl; vector<string>::iterator iter=vec.end(); --iter; while (iter != vec.begin()) { cout << *iter << " "; --iter; }
źródło
vec
odnosi się do pustego wektora!Ponieważ nie chcę wprowadzać nowej składni C ++ podobnej do obcych, a po prostu chcę zbudować na istniejących prymitywach, poniższe fragmenty wydają się działać:
#include <vector> #include <iostream> int main (int argc,char *argv[]) { std::vector<int> arr{1,2,3,4,5}; std::vector<int>::iterator it; // iterate forward for (it = arr.begin(); it != arr.end(); it++) { std::cout << *it << " "; } std::cout << "\n************\n"; if (arr.size() > 0) { // iterate backward, simple Joe version it = arr.end() - 1; while (it != arr.begin()) { std::cout << *it << " "; it--; } std::cout << *it << " "; } // iterate backwards, the C++ way std::vector<int>::reverse_iterator rit; for (rit = arr.rbegin(); rit != arr.rend(); rit++) { std::cout << *rit << " "; } return 0; }
źródło
arr
odnosi się do pustego wektora!