¿Cuál es la mejor manera de hacer un bucle inverso ‘for’ con un índice sin firmar?

3 minutos de lectura

avatar de usuario
Aurón

Mi primer intento de bucle for inverso que hace algo n veces fue algo como:

for ( unsigned int i = n-1; i >= 0; i-- ) {
    ...     
}

Esta falla porque en aritmética sin signo i se garantiza que siempre es mayor o igual que cero, por lo tanto, la condición del ciclo siempre será verdadera. Afortunadamente, el compilador gcc me advirtió sobre una ‘comparación sin sentido’ antes de que tuviera que preguntarme por qué el bucle se ejecutaba infinitamente.


Estoy buscando una forma elegante de resolver este problema teniendo en cuenta que:

  1. Debería ser un bucle for al revés.
  2. El índice de bucle no debe estar firmado.
  3. n es una constante sin signo.
  4. No debe basarse en la aritmética de anillos ‘oscura’ de los enteros sin signo.

¿Algunas ideas? Gracias 🙂

  • si n > mayor valor positivo representable por int.

    –Pete Kirkham

    20 de marzo de 2009 a las 11:38

  • Luego usa un largo. Y si su matriz es demasiado grande por mucho tiempo, tiene problemas más serios que sin firmar 🙂

    – pax diablo

    20 de marzo de 2009 a las 11:41

  • ¿Semántica, tal vez? Como nunca debería estar bajo cero.

    – Aurón

    20 de marzo de 2009 a las 11:42

  • ¿Quién dijo algo sobre una matriz?

    luego

    20 de marzo de 2009 a las 11:42

  • @Pax: por ejemplo, este podría ser un código para un procesador de 16 bits, donde i comienza por encima de 32767. Usar algo más grande que un int sin firmar sería ineficiente.

    –Steve Melnikoff

    21 de marzo de 2009 a las 17:07

avatar de usuario
esquizo

Qué tal si:

for (unsigned i = n ; i-- > 0 ; )
{
  // do stuff with i
}

  • @Auron, n suele ser la longitud de una matriz, por lo que en la mayoría de los casos no desea i == n.

    – quinmars

    20 de marzo de 2009 a las 12:07

  • En lo que a mí respecta, este es el idioma estándar para el bucle inverso, me sorprende que la gente no lo haya conocido antes.

    – bobince

    20 de marzo de 2009 a las 13:33

  • Incluso puedes escribirlo como i --> 0 lo cual indicará visualmente la intención 😉

    – Patrick Schlüter

    11 de mayo de 2011 a las 8:22

  • @HeathHunnicutt: la aritmética de enteros sin signo tiene una semántica de “módulo” bien definida. Los resultados fuera de los límites se ajustarán silenciosamente.

    – Mankarse

    16 de octubre de 2011 a las 9:08

  • Los enteros sin signo @HeathHunnicutt nunca se desbordan ni se desbordan en C. Solo para enteros con signo, los desbordamientos y desbordamientos tienen un comportamiento indefinido. De la norma: unsigned arithmetic does not overflow because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type..

    – Ruslán

    3 de marzo de 2016 a las 5:21


for ( unsigned int loopIndex = n; loopIndex > 0; --loopIndex ) {
    unsigned int i = loopIndex - 1;
    ...
} 

o

for ( unsigned int loopIndex = 0; loopIndex < n; ++loopIndex ) {
    unsigned int i = n - loopIndex - 1;
    ...
} 

  • ¡Ay! Accidentalmente borré un comentario cuando intentaba borrar el mío. Obtuve de mi caché: “Un ejemplo clásico de nombres de variables largos que hacen que el código sea menos legible, en mi humilde opinión”. — lo siento, no puedo encontrar un deshacer

    – Lou Franco

    20 de marzo de 2009 a las 11:40

for ( unsigned int i = n; i != 0; i-- ) {
    // do something with i - 1
    ...     
}

Tenga en cuenta que si usa C++ además de C, usar != es un buen hábito para cuando cambie a usar iteradores, donde <= etc. puede no estar disponible.

  • Además, cuando cualquier código dentro del ciclo disminuye aún más, pronto notará un ciclo infinito. (y es más fácil razonar sobre)

    – xtofl

    20 de marzo de 2009 a las 12:38

avatar de usuario
idz

¿Por qué no simplemente:

unsigned int i = n;
while(i--)
{ 
    // use i
}

Esto cumple con todos los requisitos enumerados en el cuerpo de la pregunta. No utiliza nada que pueda fallar en la revisión del código o violar un estándar de codificación. La única objeción que pude ver es si el OP realmente insistiera en un for bucle y no una forma sencilla de generar i = (n-1) .. 0.

avatar de usuario
Pete Kirkham

tendería a usar

 for ( unsigned int i = n; i > 0; )  {
    --i;
    ...     
 }

es casi lo mismo que la respuesta de skizz (se pierde una disminución final innecesaria, pero el compilador debería optimizarla), y en realidad aprobará la revisión del código. Cada estándar de codificación con el que he tenido que trabajar ha tenido una regla condicional de no mutación.

  • Ahora que‘sa mutilado para construir, ¡para ser reemplazado rápidamente por una declaración while! -1 por eso.

    – xtofl

    20 de marzo de 2009 a las 12:39

  • xtofl — No, no es una instrucción while. Skizz muestra la mejor manera. Una declaración while necesita que su variable de bucle se declare fuera de su alcance, en una declaración independiente.

    – Svante

    20 de marzo de 2009 a las 12:55

  • Desafortunadamente, la respuesta de Skizz no pasaría la revisión del código en la mayoría de las tiendas.

    –Pete Kirkham

    20 de marzo de 2009 a las 13:03

  • “sin mutación en condicional” – ¡Pensé que éramos programadores, no monos (de código)! Las reglas deben romperse dondequiera que el código sufra por ello. Usar un indexador ‘i-1’ es mucho peor que un condicional mutante. Los aros por los que saltan algunas de estas respuestas son simplemente desagradables.

    – Skizz

    20 de marzo de 2009 a las 14:22

  • Estoy de acuerdo en que usar un indexador i-1 es peor.

    –Pete Kirkham

    20 de marzo de 2009 a las 14:48

avatar de usuario
vartec

for ( unsigned int i = n; i > 0; i-- ) {
    ...  
    i-1 //wherever you've been using i   
}

  • Ahora que‘sa mutilado para construir, ¡para ser reemplazado rápidamente por una declaración while! -1 por eso.

    – xtofl

    20 de marzo de 2009 a las 12:39

  • xtofl — No, no es una instrucción while. Skizz muestra la mejor manera. Una declaración while necesita que su variable de bucle se declare fuera de su alcance, en una declaración independiente.

    – Svante

    20 de marzo de 2009 a las 12:55

  • Desafortunadamente, la respuesta de Skizz no pasaría la revisión del código en la mayoría de las tiendas.

    –Pete Kirkham

    20 de marzo de 2009 a las 13:03

  • “sin mutación en condicional” – ¡Pensé que éramos programadores, no monos (de código)! Las reglas deben romperse dondequiera que el código sufra por ello. Usar un indexador ‘i-1’ es mucho peor que un condicional mutante. Los aros por los que saltan algunas de estas respuestas son simplemente desagradables.

    – Skizz

    20 de marzo de 2009 a las 14:22

  • Estoy de acuerdo en que usar un indexador i-1 es peor.

    –Pete Kirkham

    20 de marzo de 2009 a las 14:48

¿Quizás de esta manera? En mi humilde opinión es claro y legible. Puede omitir if(n>=1) si se conoce implícitamente de alguna manera.

if(n>=1) {
    // Start the loop at last index
    unsigned int i = n-1;
    do {
       // a plus: you can use i, not i-1 here
    } while( i-- != 0 );
}

Otra version:

if(n>=1) {
    unsigned int i = n;
    do {
       i--;

    } while( i != 0 );
}

El primer código sin la instrucción if se vería así:

unsigned int i = n-1;
do {

} while( i-- != 0 );

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad