web-dev-qa-db-fra.com

Itérer sur un vecteur en sens inverse

Je dois parcourir un vecteur de la fin au début. La façon "correcte" est

for(std::vector<SomeT>::reverse_iterator rit = v.rbegin(); rit != v.rend(); ++rit)
{
    //do Something
}

Lorsque quelque chose implique de connaître l’indice réel, quelques calculs doivent être effectués avec rit pour l’obtenir, comme index = v.size() - 1 - (rit - v.rbegin)

De toute façon, si l’indice est nécessaire, alors je crois fermement qu’il est préférable d’itérer l’utilisation de cet index

for(int i = v.size() - 1; i >= 0; --i)
{
    //do something with v[i] and i; 
}

Ceci avertit que i est signé et que v.size() est non signé .

for(unsigned i = v.size() - 1; i >= 0; --i) est juste incorrect du point de vue fonctionnel, car il s'agit essentiellement d'une boucle sans fin :)

Quel est le bon moyen esthétique de faire ce que je veux faire? 

  • est sans avertissement
  • n'implique pas de moulages
  • n'est pas trop verbeux

J'espère que je ne cherche pas quelque chose qui n'existe pas :)

32
Armen Tsirunyan

Comme vous l'avez noté, le problème avec une condition de i >= 0 lorsqu'il n'est pas signé est que la condition est toujours vraie. Au lieu de soustraire 1 lorsque vous initialisez i, puis à nouveau après chaque itération, soustrayez 1 après avoir vérifié la condition de boucle:

for (unsigned i = v.size(); i-- > 0; )

J'aime ce style pour plusieurs raisons:

  • Bien que i soit renvoyé à UINT_MAX à la fin de la boucle, il ne fait pas compter ce comportement - cela fonctionnerait de la même manière si les types étaient signés. Compter sur des enveloppes non signées est un peu comme un piratage pour moi.
  • Il appelle size() exactement une fois.
  • Il n'utilise pas >=. Chaque fois que je vois cet opérateur dans une boucle for, je dois le relire pour m'assurer qu'il ne s'agit pas d'une erreur off-by-one.
  • Si vous modifiez l'espacement dans la condition, vous pouvez lui faire utiliser l'opérateur "va à" .
54
Rob Kennedy

Il n'y a rien pour arrêter votre boucle reverse_iterator en utilisant également l'index, comme décrit dans plusieurs autres réponses. De cette façon, vous pouvez utiliser l'itérateur ou l'index selon vos besoins dans la partie // do the work, pour un coût supplémentaire minime.

size_t index = v.size() - 1;
for(std::vector<SomeT>::reverse_iterator rit = v.rbegin(); 
    rit != v.rend(); ++rit, --index)
{
  // do the work
}

Bien que je sois curieux de savoir pourquoi vous avez besoin de l’index. Accéder à v[index] équivaut à accéder à *rit.

14
Steve Townsend

être esthétiquement agréable! ;)

for(unsigned i = v.size() - 1; v.size() > i; --i)
5
Nim

Je préférerais la variante d'itérateur inverse, car il est toujours facile à interpréter et permet d'éviter les erreurs liées à l'index.

Parfois, vous pouvez simplement utiliser le BOOST_REVERSE_FOREACH, ce qui donnerait à votre code l'aspect suivant:

reverse_foreach (int value, vector) {
   do_something_with_the_value;
}

En fait, vous pouvez toujours utiliser des instructions foreach pour ces types de boucles, mais elles deviennent alors un peu non évidentes:

size_t i = 0;

foreach (int value, vector) {
   do_something;
   ++i;
}
2
Yippie-Ki-Yay

Essayez un do pendant que:

std::vector<Type> v;
// Some code 
if(v.size() > 0)
{
    unsigned int i = v.size() - 1;
    do
    {
        // Your stuff
    }
    while(i-- > 0);
}
0

Salut, je pense que la meilleure façon d'utiliser itérateur comme vous utilisez dans le premier échantillon et si vous avez besoin d'obtenir un index d'itérateur vous pouvez utiliser.

0
Sanja Melnichuk