Bir vektörü baştan sona yinelemek mümkün mü?
for (vector<my_class>::iterator i = my_vector.end();
i != my_vector.begin(); /* ?! */ ) {
}
Yoksa bu sadece böyle bir şeyle mümkün mü?
for (int i = my_vector.size() - 1; i >= 0; --i) {
}
Bir vektörü baştan sona yinelemek mümkün mü?
for (vector<my_class>::iterator i = my_vector.end();
i != my_vector.begin(); /* ?! */ ) {
}
Yoksa bu sadece böyle bir şeyle mümkün mü?
for (int i = my_vector.size() - 1; i >= 0; --i) {
}
Yanıtlar:
En iyi yol şudur:
for (vector<my_class>::reverse_iterator i = my_vector.rbegin();
i != my_vector.rend(); ++i ) {
}
rbegin()
/ rend()
özellikle bu amaç için tasarlandı. (Ve evet, a'yı artırmak reverse_interator
onu geriye doğru hareket ettirir.)
Şimdi, teoride, yönteminiz ( begin()
/ end()
& kullanarak --i
) işe std::vector
yarar, yineleyicinin çift yönlü olması, ancak unutmayın, end()
son öğe değildir - bu, son öğenin ötesindedir, bu nedenle önce azaltmanız gerekir ve siz ulaştığınızda yapılır begin()
- ancak yine de işlemlerinizi yapmanız gerekir.
vector<my_class>::iterator i = my_vector.end();
while (i != my_vector.begin())
{
--i;
/*do stuff */
}
GÜNCELLEME: Görünüşe göre for()
döngüyü bir while()
döngüye yeniden yazarken çok agresif davrandım. (Önemli --i
olan, başlangıçta olmasıdır.)
--i
Konteynır boşsa bunun büyük bir soruna neden olacağını yeni fark ettim ... Döngüye girmeden önce do - while
kontrol etmek mantıklı (my_vector.begin() != my_vector.end())
.
do-while
sadece bir while
döngü yerine bir döngü kullanıyorsunuz? O zaman boş vektörler için özel bir kontrole ihtiyacınız olmaz.
auto
Daha iyi okunabilirlik için kullanmak üzere yanıtı güncelleyebilir misiniz ?
Kapalı-açık aralıklarla ters yineleme için iyi bilinen "model" aşağıdaki gibidir
// Iterate over [begin, end) range in reverse
for (iterator = end; iterator-- != begin; ) {
// Process `*iterator`
}
veya tercih ederseniz,
// Iterate over [begin, end) range in reverse
for (iterator = end; iterator != begin; ) {
--iterator;
// Process `*iterator`
}
Bu şablon, örneğin işaretsiz bir dizin kullanarak bir diziyi tersine endekslemek için kullanışlıdır.
int array[N];
...
// Iterate over [0, N) range in reverse
for (unsigned i = N; i-- != 0; ) {
array[i]; // <- process it
}
(Bu kalıba aşina olmayan kişiler genellikle işaretli özellikle işaretsiz türlerin bir şekilde ters dizinleme için "kullanılamaz" olduklarına yanlış bir şekilde inanmaları nedeniyle, özellikle dizi dizinleme için tam sayı türleri ederler)
Bir "kayan işaretçi" tekniği kullanılarak bir dizi üzerinde yineleme yapmak için kullanılabilir
// Iterate over [array, array + N) range in reverse
for (int *p = array + N; p-- != array; ) {
*p; // <- process it
}
veya sıradan (ters değil) bir yineleyici kullanarak bir vektör üzerinde ters yineleme için kullanılabilir
for (vector<my_class>::iterator i = my_vector.end(); i-- != my_vector.begin(); ) {
*i; // <- process it
}
--end()
end()
. Başlıyor gibi görünseler bile end()
, her zaman ilk erişimden önce yineleyiciyi azaltmaya özen gösterirler.
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
Dört kez test ediyorsunuz - ikisi bir döngü içinde. Elbette, bir boole testi çok hızlıdır, ancak yine de neden çalışmak zorunda değilsiniz? Özellikle, tek amaç kodu okunamaz hale getirmek gibi göründüğü için. iki ayrı döngü kullanmaya ne dersiniz? 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
ancak şablondaki şablondan kurtulmak istedim doStuff()
. Yine de, sahip olduğunuz iki if
s ile, birincisinde diğer yolu döndürerek yapılabilir .
C ++ 20'den başlayarak, a std::ranges::reverse_view
ve aralık tabanlı bir for-döngüsü kullanabilirsiniz:
#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 << ",";
}
Ya da
for(auto& i : vec | views::reverse)
Ne yazık ki, yazı yazarken (Ocak 2020) hiçbir büyük derleyici aralık kitaplığını uygulamaz, ancak Eric Niebler'in aralıkları-v3'e başvurabilirsiniz :
#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;
}
template<class It>
std::reverse_iterator<It> reversed( It it ) {
return std::reverse_iterator<It>(std::forward<It>(it));
}
Sonra:
for( auto rit = reversed(data.end()); rit != reversed(data.begin()); ++rit ) {
std::cout << *rit;
Alternatif olarak C ++ 14'te şunları yapın:
for( auto rit = std::rbegin(data); rit != std::rend(data); ++rit ) {
std::cout << *rit;
C ++ 03 / 11'de çoğu standart konteynerin bir .rbegin()
ve .rend()
yöntemi de vardır.
Son olarak, aralık adaptörünü backwards
şu şekilde yazabilirsiniz :
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) ) };
}
ve şimdi bunu yapabilirsiniz:
for (auto&& x : backwards(ctnr))
std::cout << x;
bence oldukça güzel.
Ters yineleyiciler kullanın ve ile arasında döngü rbegin()
yapınrend()
Yakk'in sonundaki geriye dönük yineleyiciyi seviyorum - Adam Nevraumont'un cevabı, ama ihtiyacım olan şey için karmaşık görünüyordu, ben de şunu yazdım:
template <class T>
class backwards {
T& _obj;
public:
backwards(T &obj) : _obj(obj) {}
auto begin() {return _obj.rbegin();}
auto end() {return _obj.rend();}
};
Bunun gibi normal bir yineleyici alabilirim:
for (auto &elem : vec) {
// ... my useful code
}
ve tersten yinelemek için bunu buna değiştirin:
for (auto &elem : backwards(vec)) {
// ... my useful code
}
Her yapının kullanımına izin veren ve yalnızca C ++ 14 std kitaplığına dayanan süper basit bir uygulama:
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));
}
Bu, bir rbegin () ve rend () sağlayan şeylerin yanı sıra statik dizilerle de çalışır.
std::vector<int> collection{ 5, 9, 15, 22 };
for (auto e : backwards(collection))
;
long values[] = { 3, 6, 9, 12 };
for (auto e : backwards(values))
;
Boost Kitaplığını kullanabiliyorsanız, aşağıdakileri dahil ederek aralık adaptörünü sağlayan Boost.Range vardır :reverse
#include <boost/range/adaptor/reversed.hpp>
Ardından, bir C ++ 11'in aralık for
döngüsü ile birlikte aşağıdakileri yazabilirsiniz:
for (auto& elem: boost::adaptors::reverse(my_vector)) {
// ...
}
Bu kod yineleyici çiftini kullanan koddan daha kısa olduğundan, dikkat edilmesi gereken daha az ayrıntı olduğundan daha okunabilir ve hatalara daha az eğilimli olabilir.
boost::adaptors::reverse
çok faydalıdır!
bu kodu kullan
//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;
}
vec
boş bir vektöre atıfta bulunursa, korkunç şekilde başarısız olur !
Uzaylı benzeri yeni C ++ sözdizimi tanıtmak istemediğimden ve sadece mevcut ilkelleri geliştirmek istediğimden, aşağıdaki parçacıklar işe yarıyor gibi görünüyor:
#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;
}
arr
boş bir vektöre atıfta bulunursa, korkunç şekilde başarısız olur !