2010-11-17 17 views
28

Necesito iterar sobre un vector desde el final hasta el principio. La forma "correcta" esIteración sobre un vector en dirección inversa

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

Cuando doSomething implica conocer el índice real, entonces algunos cálculos se deben hacer con rit para obtenerlo, como index = v.size() - 1 - (rit - v.rbegin)

Si se necesita el índice de todos modos, entonces fuertemente cree que es mejor para iterar usando ese índice

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

Esto da una advertencia de que está firmado y iv.size() no tiene signo. Cambiar a

for(unsigned i = v.size() - 1; i >= 0; --i) es sólo funcionalmente mal, porque este es esencialmente un bucle sin fin :)

¿Qué es un estéticamente buena manera de hacer lo que quiero hacer, que

  • está advirtiendo de libre
  • no implica arroja
  • no es excesivamente prolijo

espero no estoy buscando algo que no existe :)

+0

condición de bucle 'i! = Std :: numeric_limits :: max()' ... o usa UINT_MAX si crees que es verboso. – smerlin

+0

Hasta ahora, creo que hacer un reparto se ve como la solución más limpia :-) –

+0

en realidad estoy de acuerdo con David – smerlin

Respuesta

48

Como habrás notado, el problema con una condición de i >= 0 cuando no está firmado es que la condición siempre es verdadera. En lugar de restar 1 al inicializar i y luego otra vez después de cada iteración, se resta 1 después de comprobar la condición del bucle:

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

me gusta este estilo por varias razones:

  • Aunque i se ajustará alrededor de UINT_MAX al final del ciclo, no hace referencia a en ese comportamiento; funcionaría igual si los tipos se firmaran. Depender de un envolvente sin firma me parece un poco complicado.
  • Llama a size() exactamente una vez.
  • No usa >=. Cada vez que veo ese operador en un bucle for, tengo que volver a leerlo para asegurarme de que no haya un error "uno por uno".
  • Si cambia el espaciado en el condicional, puede hacer que use el "goes to" operator.
+0

+1 limpio =), supongo que siempre hay ¡margen de mejora! – Nim

+2

¿no debería ser size_t que unsigned? – dynamic

+1

Eso podría ser mejor si realmente anticipa tener más elementos de los que caben en un 'unsigned', @ Yes123, pero el tipo formalmente correcto sería' std :: vector :: size_type', o, hoy en día, simplemente 'auto' . –

-3

creo que:

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

está bien si marca

!v.empty() 

anterior.

+0

-1 Este es un bucle infinito también :) –

+0

@ HardCoder1986: Piénselo de nuevo :) –

+0

@Armen OH SHI -... –

2

Preferiría la variante del iterador inverso, porque aún es fácil de interpretar y permite evitar los errores relacionados con el índice.

A veces se puede simplemente utilizar el BOOST_REVERSE_FOREACH, lo que haría que su código de mirar la siguiente manera:

reverse_foreach (int value, vector) { 
    do_something_with_the_value; 
} 

En realidad hablando, siempre se puede utilizar foreach declaraciones para este tipo de bucles, pero luego se convierten en un poco no obvia:

size_t i = 0; 

foreach (int value, vector) { 
    do_something; 
    ++i; 
} 
0

probar un Do While:

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

Lo siento, mi mal ... –

+0

Bien, no hay problema;). Probé el do y funciona. No digo que sea la mejor solución, pero está disponible. –

0

Hola creo que un mejor uso iterador manera como se utiliza en la primera muestra, y si necesita obtener el índice iterador puede utilizar std :: distancia para calcular que, si entiendo su pregunta

0

condición de bucle i != std::numeric_limits<unsigned>::max() ... o usa UINT_MAX si crees que es verboso. o de otra manera:
for(unsigned j=0, end=v.size(), i=end-1; j<end; --i, ++j)
o
for(unsigned end=v.size(), i=end-1; (end-i)<end; --i)

5

para ser estéticamente agradable! ;)

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

@Armen, hmm, pero sin signo (por defecto int) * podría ser * más pequeño que el 'size_type' de' vector', por lo que podría tener más de 4bn entradas en su vector, esa es una pregunta diferente ...:) ... está bien, está bien ... doy por vencido ... – Nim

+2

+1, eso es bastante inteligente Nim. –

+0

¿Y si v.size() == UINT_MAX? Sé que esto nunca sucede en la práctica, pero es un error teórico –

12

No hay nada para detener el bucle reverse_iterator también utilizando el índice como se describe en varias otras respuestas. De esta forma, puede usar el iterador o el índice según sea necesario en la parte // do the work, por un costo adicional mínimo.

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

Aunque tengo curiosidad por saber para qué necesita el índice. Acceder al v[index] es lo mismo que acceder al *rit.

+0

Esta es en realidad la única respuesta correcta para cualquier tipo subyacente que no sea de acceso aleatorio. Suponiendo que 'std :: vector ' se escribe en otro lugar prefiero usar el iterador correcto en caso de que luego decida usar una lista, multiset, mapa, etc. –

-2
for (it = v.end()-1; it != v.begin()-1; --it) 
{ 
} 

El "goes to" operator definitivamente se mete con la cabeza.

+0

Tengo curiosidad sobre por qué esto fue votado como negativo. – Floella

+1

@Floella AFAIK, esto no funciona (restar enteros no es compatible con iteradores). Para iterar al revés, creo que tienes que usar 'std :: vector :: reverse_iterator' * a la * https://stackoverflow.com/a/4206142/5835083, o usar' std :: distance' si realmente insiste en usar la resta. –

Cuestiones relacionadas