É possível iterar um vetor do fim ao começo?
for (vector<my_class>::iterator i = my_vector.end();
i != my_vector.begin(); /* ?! */ ) {
}
Ou isso só é possível com algo assim:
for (int i = my_vector.size() - 1; i >= 0; --i) {
}
É possível iterar um vetor do fim ao começo?
for (vector<my_class>::iterator i = my_vector.end();
i != my_vector.begin(); /* ?! */ ) {
}
Ou isso só é possível com algo assim:
for (int i = my_vector.size() - 1; i >= 0; --i) {
}
Respostas:
A melhor maneira é:
for (vector<my_class>::reverse_iterator i = my_vector.rbegin();
i != my_vector.rend(); ++i ) {
}
rbegin()
/ rend()
Foram especialmente concebidos para o efeito. (E sim, incrementar a reverse_interator
move para trás.)
Agora, em teoria, seu método (usando begin()
/ end()
& --i
) funcionaria, std::vector
o iterador sendo bidirecional, mas lembre-se, end()
não é o último elemento - é um além do último elemento, então você teria que decrementar primeiro, e você é feito quando você chegar begin()
- mas você ainda terá que fazer o seu processamento.
vector<my_class>::iterator i = my_vector.end();
while (i != my_vector.begin())
{
--i;
/*do stuff */
}
ATUALIZAÇÃO: Eu fui aparentemente muito agressivo ao reescrever o for()
loop em um while()
loop. (A parte importante é que o --i
está no início.)
--i
isso causará um grande problema se o recipiente estiver vazio ... Antes de entrar no do - while
loop, faz sentido verificar (my_vector.begin() != my_vector.end())
.
do-while
loop em vez de apenas um while
loop? Então você não precisaria de nenhuma verificação especial para vetores vazios.
auto
para melhor legibilidade?
O "padrão" bem estabelecido para a iteração reversa através de intervalos fechados-abertos é o seguinte
// Iterate over [begin, end) range in reverse
for (iterator = end; iterator-- != begin; ) {
// Process `*iterator`
}
ou, se preferir,
// Iterate over [begin, end) range in reverse
for (iterator = end; iterator != begin; ) {
--iterator;
// Process `*iterator`
}
Este padrão é útil, por exemplo, para indexação reversa de uma matriz usando um índice não assinado
int array[N];
...
// Iterate over [0, N) range in reverse
for (unsigned i = N; i-- != 0; ) {
array[i]; // <- process it
}
(Pessoas não familiarizadas com este padrão muitas vezes insistem em usar tipos inteiros assinados para indexação de array, especificamente porque acreditam incorretamente que tipos não assinados são de alguma forma "inutilizáveis" para indexação reversa)
Ele pode ser usado para iterar sobre uma matriz usando uma técnica de "ponteiro deslizante"
// Iterate over [array, array + N) range in reverse
for (int *p = array + N; p-- != array; ) {
*p; // <- process it
}
ou pode ser usado para iteração reversa sobre um vetor usando um iterador comum (não reverso)
for (vector<my_class>::iterator i = my_vector.end(); i-- != my_vector.begin(); ) {
*i; // <- process it
}
--end()
end()
. Mesmo que pareçam começar em end()
, eles sempre se certificam de diminuir o iterador antes do primeiro acesso.
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
quatro vezes - duas delas dentro de um loop. Claro, testar um booleano é muito rápido, mas ainda assim, por que trabalhar você não precisa? Especialmente porque o único propósito parece ser tornar o código ilegível. que tal usarmos dois loops separados? 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
s, mas eu queria me livrar do modelo no doStuff()
. Ainda é possível, porém, com os dois if
s que você tem, fazendo o loop ao contrário no primeiro.
Começando com c ++ 20, você pode usar um std::ranges::reverse_view
e um loop for baseado em intervalo:
#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 << ",";
}
Ou mesmo
for(auto& i : vec | views::reverse)
Infelizmente, no momento da escrita (janeiro de 2020), nenhum grande compilador implementou a biblioteca de intervalos, mas você pode recorrer a intervalos-v3 de 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;
}
template<class It>
std::reverse_iterator<It> reversed( It it ) {
return std::reverse_iterator<It>(std::forward<It>(it));
}
Então:
for( auto rit = reversed(data.end()); rit != reversed(data.begin()); ++rit ) {
std::cout << *rit;
Como alternativa, em C ++ 14, basta fazer:
for( auto rit = std::rbegin(data); rit != std::rend(data); ++rit ) {
std::cout << *rit;
Em C ++ 03/11 a maioria dos recipientes convencionais têm uma .rbegin()
e .rend()
método bem.
Finalmente, você pode escrever o adaptador de alcance da backwards
seguinte maneira:
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) ) };
}
e agora você pode fazer isso:
for (auto&& x : backwards(ctnr))
std::cout << x;
o que eu acho muito bonito.
Use iteradores reversos e loop de rbegin()
pararend()
Aqui está uma implementação super simples que permite o uso de for each construct e depende apenas da biblioteca std 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));
}
Isso funciona com coisas que fornecem um rbegin () e rend (), bem como com matrizes estáticas.
std::vector<int> collection{ 5, 9, 15, 22 };
for (auto e : backwards(collection))
;
long values[] = { 3, 6, 9, 12 };
for (auto e : backwards(values))
;
Se você pode usar a Biblioteca Boost, há o Boost.Range que fornece o reverse
adaptador de intervalo incluindo:
#include <boost/range/adaptor/reversed.hpp>
Então, em combinação com um loop de intervalo do C ++ 11for
, você pode simplesmente escrever o seguinte:
for (auto& elem: boost::adaptors::reverse(my_vector)) {
// ...
}
Como esse código é mais breve do que aquele que usa o par de iteradores, ele pode ser mais legível e menos sujeito a erros, pois há menos detalhes aos quais prestar atenção.
boost::adaptors::reverse
é muito útil!
Eu gosto do iterador reverso no final de Yakk - a resposta de Adam Nevraumont, mas parecia complicado para o que eu precisava, então escrevi isto:
template <class T>
class backwards {
T& _obj;
public:
backwards(T &obj) : _obj(obj) {}
auto begin() {return _obj.rbegin();}
auto end() {return _obj.rend();}
};
Sou capaz de usar um iterador normal como este:
for (auto &elem : vec) {
// ... my useful code
}
e altere-o para iterar ao contrário:
for (auto &elem : backwards(vec)) {
// ... my useful code
}
use este código
//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
refere a um vetor vazio!
Como não quero introduzir a nova sintaxe alienígena de Marte, e simplesmente quero construir sobre primitivos existentes, os snippets abaixo parecem funcionar:
#include <vector>
#include <iostream>
int main (int argc,char *argv[])
{
std::vector<int> arr{1,2,3,4,5};
std::vector<int>::iterator it;
for (it = arr.begin(); it != arr.end(); it++) {
std::cout << *it << " ";
}
std::cout << "\n************\n";
for (it = arr.end() - 1; it != arr.begin()-1;it--) {
std::cout << *it << " ";
}
return 0;
}
arr
refere a um vetor vazio!