c++ - sentencias - Iteración inversa con una variable de bucle sin signo
sentencias de iteracion en c (9)
- Reemplace el bucle con un algoritmo.
- Use un iterador inverso en lugar de un entero.
- Cuente hacia abajo de n a 1, pero dentro del ciclo use
i-1
lugar dei
.
He estado discutiendo el uso de size_t con mis colegas. Un problema que ha surgido son los bucles que disminuyen la variable de bucle hasta que llega a cero.
Considera el siguiente código:
for (size_t i = n-1; i >= 0; --i) { ... }
Esto causa un bucle infinito debido a un envolvente de entero sin signo. ¿Qué haces en este caso? Parece muy fácil escribir el código anterior y no darse cuenta de que ha cometido un error.
Dos sugerencias de nuestro equipo son usar uno de los siguientes estilos:
for (size_t i = n-1; i != -1 ; --i) { ... }
for (size_t i = n; i-- > 0 ; ) { ... }
Pero me pregunto qué otras opciones hay ...
¿Estás usando contenedores de biblioteca estándar? Si es así me gusta reverse_iterator
vector<int> ivect;
// push, push, push...
vector<int>::reverse_iterator riter;
for(riter=riter.rbegin(); riter!=ivect.rend(); ++riter)
{
//...
}
Para una matriz sin std::reverse_iterator
puede usar un std::reverse_iterator
la clave para esto es que un puntero es un iterador:
int i[] = {1, 2, 3, 4};
typedef std::reverse_iterator<const int*> irevit;
irevit iter(i+4);
irevit end(i);
for(; iter != end; ++iter) {
cout << *iter;
}
// Prints 4321
La iteración de objetos no contigua se puede realizar almacenando los punteros de objeto en un contenedor o matriz:
struct Foo {
Foo(int i) I(i) { }
int I;
}
vector<Foo*> foos;
for(int i = 0; i < 10; ++i)
foos.push_back(new Foo(i));
typedef vector<Foo*>::const_reverse_iterator frevit;
frevit iter(foos.rbegin());
for(; iter != foos.rend(); ++iter) {
cout << (*iter)->I;
}
// Prints 9876543210
Si realmente quieres usar un size_t
, ¿por qué utilizar todo esto engaño implícitamente confuso -1 en las otras respuestas? El valor máximo de size_t
está explícitamente disponible para usar como valor de terminación:
int is[] = {1, 2, 3, 4};
int n = 3;
for (size_t i = n; i != std::numeric_limits<size_t>::max(); --i) {
cout << is[i] << endl;
}
// prints 4321
Aquí hay un indicador de una buena discusión sobre este tema.
Lo intentaré:
for( size_t i = n; i != 0; i-- ) {
// do stuff with array[ i - 1 ]
}
Los enteros sin signo están garantizados para envolverse muy bien. Simplemente implementan el módulo aritmético 2 N. Entonces, un modismo fácil de leer es este:
for (size_t i = n-1; i < n ; --i) { ... }
esto establece la variable al valor inicial que desea, muestra el sentido de la iteración (hacia abajo) y proporciona precisamente la condición en los valores que desea manejar.
Otra forma más (sin comparaciones firmadas / sin firmar):
for (size_t i = n-1; i + 1 > 0; i--)
ya que
(i + 1 > 0) === (i > -1)
Personalmente, me gusta:
for (size_t i = n; i --> 0 ;)
Tiene a) ningún gracioso -1
, b) el chequeo de condición es mnemotécnico, c) termina con un smiley adecuado.
Si le preocupa escribir accidentalmente un bucle así, algunos compiladores advertirán sobre tales cosas. Por ejemplo, gcc tiene una advertencia habilitada por la opción -Wtype-limits
(también habilitada por -Wextra
):
x.c:42: warning: comparison of unsigned expression >= 0 is always true
i != -1
depende de que -1
sea lanzado silenciosamente a un size_t
, lo cual me parece frágil, entonces, de las alternativas que presente, definitivamente iré con el de decremento posterior. Otra posibilidad (especialmente si no necesitas i
en el cuerpo del bucle pero solo necesitas iterar en una matriz en orden inverso) sería envolver la matriz en un contenedor std::
-like y usar un iterador en el contenedor , con los métodos rbegin
y rend
. Por ejemplo, Boost.Array sería compatible con la última opción.
size_t i = n-1;
do {
...
} while ( i-- != 0);
Puede envolver eso con un if (n > 0)
si es necesario.