Apakah mungkin untuk mengulang sebuah vektor dari akhir ke awal?
for (vector<my_class>::iterator i = my_vector.end();
i != my_vector.begin(); /* ?! */ ) {
}
Atau itu hanya mungkin dengan hal seperti itu:
for (int i = my_vector.size() - 1; i >= 0; --i) {
}
Jawaban:
Cara terbaik adalah:
for (vector<my_class>::reverse_iterator i = my_vector.rbegin(); i != my_vector.rend(); ++i ) { }
rbegin()
Akurend()
dirancang khusus untuk tujuan itu. (Dan ya, menambahkanreverse_interator
gerakan ke belakang.)Sekarang, secara teori, metode Anda (menggunakan
begin()
/end()
&--i
) akan berfungsi,std::vector
iterator menjadi dua arah, tetapi ingat,end()
bukan elemen terakhir - ini adalah salah satu di luar elemen terakhir, jadi Anda harus mengurangi terlebih dahulu, dan Anda selesai saat Anda mencapaibegin()
- tetapi Anda masih harus melakukan pemrosesan.vector<my_class>::iterator i = my_vector.end(); while (i != my_vector.begin()) { --i; /*do stuff */ }
UPDATE: Saya tampaknya terlalu agresif dalam menulis ulang
for()
loop menjadi satuwhile()
lingkaran. (Bagian yang penting adalah bahwa--i
ada di awal.)sumber
--i
akan menyebabkan masalah besar jika wadah kosong ... Sebelum masuk kedo - while
loop masuk akal untuk memeriksa(my_vector.begin() != my_vector.end())
.do-while
loop, bukan hanya satuwhile
loop? Maka Anda tidak memerlukan pemeriksaan khusus untuk vektor kosong.auto
agar lebih mudah dibaca?Jika Anda memiliki C ++ 11, Anda dapat memanfaatkan
auto
.for (auto it = my_vector.rbegin(); it != my_vector.rend(); ++it) { }
sumber
"Pola" yang mapan untuk perulangan balik melalui rentang buka-tertutup terlihat sebagai berikut
// Iterate over [begin, end) range in reverse for (iterator = end; iterator-- != begin; ) { // Process `*iterator` }
atau, jika Anda lebih suka,
// Iterate over [begin, end) range in reverse for (iterator = end; iterator != begin; ) { --iterator; // Process `*iterator` }
Pola ini berguna, misalnya, untuk mengindeks terbalik sebuah array menggunakan indeks yang tidak ditandatangani
int array[N]; ... // Iterate over [0, N) range in reverse for (unsigned i = N; i-- != 0; ) { array[i]; // <- process it }
(Orang yang tidak terbiasa dengan pola ini sering kali bersikeras menggunakan jenis bilangan bulat bertanda tangan untuk pengindeksan larik secara khusus karena mereka salah percaya bahwa jenis tak bertanda tangan entah bagaimana "tidak dapat digunakan" untuk pengindeksan terbalik)
Ini dapat digunakan untuk iterasi di atas array menggunakan teknik "pointer geser"
// Iterate over [array, array + N) range in reverse for (int *p = array + N; p-- != array; ) { *p; // <- process it }
atau dapat digunakan untuk iterasi balik pada vektor menggunakan iterator biasa (bukan terbalik)
for (vector<my_class>::iterator i = my_vector.end(); i-- != my_vector.begin(); ) { *i; // <- process it }
sumber
--end()
end()
. Meskipun tampaknya mulaiend()
, mereka selalu memastikan untuk mengurangi iterator sebelum akses pertama.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
empat kali - dua di antaranya di dalam satu loop. Tentu saja, menguji boolean sangat cepat, tetapi tetap saja, mengapa Anda tidak perlu bekerja? Terutama, karena satu-satunya tujuan tampaknya membuat kode tidak dapat dibaca. bagaimana kalau kita menggunakan dua loop terpisah?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
, tetapi saya ingin menyingkirkan template di filedoStuff()
. Namun tetap dapat dilakukan dengan duaif
yang Anda miliki dengan mengulang sebaliknya pada yang pertama.Dimulai dengan c ++ 20, Anda dapat menggunakan a
std::ranges::reverse_view
dan for-loop berbasis rentang:#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 << ","; }
Atau bahkan
for(auto& i : vec | views::reverse)
Sayangnya, pada saat penulisan (Jan 2020) tidak ada kompilator utama yang mengimplementasikan pustaka rentang, tetapi Anda dapat menggunakan rentang-v3 Eric Niebler :
#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; }
sumber
rend() / rbegin()
Iterator pengguna :for (vector<myclass>::reverse_iterator it = myvector.rbegin(); it != myvector.rend(); it++)
sumber
template<class It> std::reverse_iterator<It> reversed( It it ) { return std::reverse_iterator<It>(std::forward<It>(it)); }
Kemudian:
for( auto rit = reversed(data.end()); rit != reversed(data.begin()); ++rit ) { std::cout << *rit;
Atau di C ++ 14 lakukan saja:
for( auto rit = std::rbegin(data); rit != std::rend(data); ++rit ) { std::cout << *rit;
Dalam C ++ 11/03 paling kontainer standar memiliki
.rbegin()
dan.rend()
metode juga.Terakhir, Anda dapat menulis adaptor rentang
backwards
sebagai berikut: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) ) }; }
dan sekarang Anda dapat melakukan ini:
for (auto&& x : backwards(ctnr)) std::cout << x;
yang menurut saya cukup cantik.
sumber
Gunakan iterator terbalik dan putar dari
rbegin()
kerend()
sumber
Saya suka iterator mundur di akhir Yakk - jawaban Adam Nevraumont, tetapi tampaknya rumit untuk apa yang saya butuhkan, jadi saya menulis ini:
template <class T> class backwards { T& _obj; public: backwards(T &obj) : _obj(obj) {} auto begin() {return _obj.rbegin();} auto end() {return _obj.rend();} };
Saya dapat menggunakan iterator normal seperti ini:
for (auto &elem : vec) { // ... my useful code }
dan mengubahnya menjadi ini untuk mengulang secara terbalik:
for (auto &elem : backwards(vec)) { // ... my useful code }
sumber
Berikut adalah implementasi super sederhana yang memungkinkan penggunaan untuk setiap konstruksi dan hanya bergantung pada pustaka C ++ 14 std:
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)); }
Ini bekerja dengan hal-hal yang menyediakan rbegin () dan rend (), serta dengan array statis.
std::vector<int> collection{ 5, 9, 15, 22 }; for (auto e : backwards(collection)) ; long values[] = { 3, 6, 9, 12 }; for (auto e : backwards(values)) ;
sumber
Jika Anda dapat menggunakan The Boost Library, ada Boost.Range yang menyediakan
reverse
adaptor rentang dengan memasukkan:#include <boost/range/adaptor/reversed.hpp>
Kemudian, dalam kombinasi dengan range-
for
loop C ++ 11 , Anda cukup menulis yang berikut ini:for (auto& elem: boost::adaptors::reverse(my_vector)) { // ... }
Karena kode ini lebih singkat daripada yang menggunakan pasangan iterator, mungkin lebih mudah dibaca dan tidak terlalu rentan terhadap kesalahan karena lebih sedikit detail yang harus diperhatikan.
sumber
boost::adaptors::reverse
sangat bermanfaat!gunakan kode ini
//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; }
sumber
vec
mengacu pada vektor kosong!Karena saya tidak ingin memperkenalkan sintaks C ++ baru yang mirip alien, dan saya hanya ingin membangun primitif yang sudah ada, cuplikan di bawah ini tampaknya berfungsi:
#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; }
sumber
arr
mengacu pada vektor kosong!