Pregunta

Quiero saber si es posible finalizar un bucle for en C++ cuando se verifica una condición final (diferente de alcanzar el número correcto de iteraciones).Por ejemplo:

for (int i = 0; i < maxi; ++i)
    for (int j = 0; j < maxj; ++j)
        // But if i == 4 < maxi AND j == 3 < maxj, 
        // then jump out of the two nested loops.

Sé que esto es posible en Perl con las siguientes llamadas LABEL o las últimas LABEL y bloques etiquetados, ¿es posible hacerlo en C++ o debería usar un bucle while?

Gracias.

¿Fue útil?

Solución

Puede usar la palabra clave return: mueva el bucle anidado a una subrutina, invoque la subrutina para ejecutar los bucles anidados y 'regrese' de la subrutina para salir [todos] los bucles.

Otros consejos

A pesar de la "goto argumentos considerados perjudiciales, éste parece el lugar perfecto para goto.Eso es esencialmente lo que estás haciendo en Perl.En serio...considere las alternativas:

Variables de estado adicionales


for (int i=0; i<maxi; ++i) {
    bool leaveLoop = false;
    for (int j=0; j<maxj; ++j) {
        if (i == 4 && j == 3) {
            leaveLoop = true;
            break; // leave the inner loop
        }
    }
    if (leaveLoop) {
        break; // leave the outside loop
    }
}

Salir por excepción


try {
    for (int i=0; i<maxi; ++i) {
        for (int j=0; j<maxj; ++j) {
            if (i == 4 && j == 3) {
                throw leave_loop();
            }
        }
    }
} catch (leave_loop const&) {
}

Lógica compleja


int j = 0;
for (int i=0; i<maxi && !(i==4 && j==3); ++i) {
    for (j=0; j<maxj && !(i==4 && j==3); ++j) {
        // inner loop
    }
}

goto


for (int i=0; i<maxi; ++i) {
    for (int j=0; j<maxj; ++j) {
        if (i==4 && j==3) {
            goto leave_loop;
        }
    }
}
leave_loop:

¿Es el último menos claro?No creo que lo sea.¿Es más frágil?En mi humilde opinión, los demás son bastante propensos a errores y frágiles en comparación con el goto versión.Lamento estar en la tribuna aquí, pero esto es algo que me ha molestado por un tiempo;)

Lo único que debes tener en cuenta es que goto y las excepciones son bastante similares.Ambos abren la posibilidad de que se filtren recursos y, lo que no sea, trátelos con cuidado.

Permítanme decir esto tan enfáticamente (pero cortésmente ;-) como pueda: La construcción for en lenguaje c no se trata de contar.

La expresión de prueba que determina si continuar puede ser cualquier cosa relevante para el propósito del ciclo; la expresión de actualización no tiene que ser " agregue uno a un contador " ;.

for (int i = 0, j = 0; i < maxi && j < maxj && i != 4 && j != 3;) {
    if (j < maxj) {
        ++j;
    } else {
        j = 0;
        ++i;
    }
}

sería una forma (bastante arbitraria) de reescribir.

El punto es que si establecer alguna condición es el punto de una interacción, generalmente es posible escribir un bucle (usando while o <=>) de una manera que indique la condición de continuar / terminar más explícitamente.

(Si pudieras publicar una descripción de lo que realmente está sucediendo, es probable que escriba algo que no parezca tan arbitrario como el anterior).

No puede saltar de dos bucles con una sola instrucción de interrupción, pero puede usar un goto para saltar desde el bucle interno justo afuera.

Si el goto está localizado y significa que hay menos lógica que de lo contrario, creo que es un código perfectamente aceptable. Tener variables de marca adicionales o elevar la variable iteradora fuera del bucle interno para que pueda compararla en el bucle externo no facilita la comprensión del código en mi humilde opinión.

De todas las sugerencias anteriores, evitaría usar el mecanismo try / catch porque las excepciones deben reservarse para circunstancias excepcionales y no para el flujo de control normal.

Usar dos saltos está bien si puede crear la segunda condición de manera apropiada. Usar un booleano para este propósito también sería bueno, e incluso podría conectarlo a la condición de cada bucle for. Por ejemplo:

bool exit_loops = false;
for (int a = 0; a < A && !exit_loops; ++a)
{
    for (int b = 0; b < B && !exit_loops; ++b)
    {
        if (some_condition) exit_loops = true;
    }
}

Aunque si está utilizando más de dos bucles, podría ser más apropiado envolverlos en una función y simplemente usar return para salir de la función (y todos los bucles también). Por otra parte, podría refactorizar el código de una manera que pueda eliminar todos los bucles menos uno, ya sea llamando a una función para realizar el código del bucle interno, etc.

Por último, no tenga miedo de usar un goto en esta circunstancia, por lo general, los goto son una mala programación no estructurada, pero en algunos casos (como este) son muy útiles.

bool done = false;

for (int i = 0; i < maxi && !done; ++i)
    for (int j = 0; j < maxj && !done; ++j)
        if (i == 4 && i < maxi && j == 3 && j < maxj )
             done = true;
        else {
        }

O podrías simplemente ir a. O no :-)

No puedes saltar así en C / C ++:

for (...)
{
  for (...)
  {
    // from here...
  }
}
// ...to here

sin el uso de goto. Necesita una construcción como:

for (...)
{
  bool
    exit = false;

  for (...)
  {
    if (do_exit)
    {
      exit = true; // or set outer loop counter to end value
      break;
    }
  }
  if (exit)
  {
    break;
  }
}

Alternativamente, use tirar y atrapar, pero eso no es bueno ya que tirar realmente debería usarse para excepciones y no para control de flujo.

Una forma limpia es hacer que el bucle interno sea una función:

bool F ()
{
  if inner loop terminates, return false else return true
}

void G ()
{
  for (...)
  {
    if (!F ())
    {
      break;
    }
  }
}
for (int i = 0; i < maxi; ++i)
{
    int j = 0;
    for (j = 0; j < maxj; ++j)
    {
         if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
             break; // exit inner loop
    }
    if (i == 4 && j == 3) // i < maxi and j < maxj otherwise we would not be here
        break; // exit outer loop
}

Puede usar una declaración goto , pero eso generalmente se considera una mala práctica.

Su otra opción es hacer algo como esto

int i;
int j = 0;
for (i = 0; i < maxi && !(i==4 && j==3); ++i)
    for (j = 0; j < maxj && !(i==4 && j==3); ++j)

Leer el código no debería ser como leer un libro de detectives (que siempre debe ser resuelto) ...

un ejemplo:

Java:

iterate_rows:
for (int i = 0; i < maxi; ++i)
{       
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            break iterate_rows;
        else
            continue iterate_rows;
    }   
}

No necesita descubrir qué hacen los iterate_rows, solo léalo.

C ++:

//iterate_rows:
for (int i = 0; i < maxi; ++i)
{
    for (int j = 0; j < maxj; ++j)
    {
        if (i == 4 < maxi && j == 3 < maxj) 
            goto break_iterate_rows;
        else
            goto continue_iterate_rows;
    }

continue_iterate_rows:;
}
break_iterate_rows:;

goto break_iterate_rows es solo una versión visible de break iterate_rows

Si limita su uso de goto y etiquetas solo en este tipo de código, no descubrirá la intención. Limitar el uso de goto y etiquetas en este tipo de código le hará leer el código, no analizarlo ni descifrarlo. No se te acusará de ser un mal programador.

Y si realmente limita sus gotos en este tipo de código, podrá desarrollar el hábito de no necesitar descubrir qué hacen esos malditos gotos en su código. El beneficio adicional es que no tiene que introducir booleanos y rastrearlos (lo que, en mi opinión, lo lleva a detectar el código , haciéndolo un poco ilegible, lo que anula el propósito de evitar gotos)

P.S.

Empareje esas etiquetas con los comentarios (antes del bucle), cuando lea más allá de esas líneas con la declaración goto ya sabe la intención de esos gotos

Tengo algunas sugerencias:

  1. lanzar .... coloque los dos bucles dentro de un & "; intente {} &"; y luego " catch " el " tirar " en la condición.

  2. coloca los dos bucles en un método y vuelve a la condición.

  3. Goto no es malo, es el uso que la gente le da también ... Puedes usar " goto " Puede ser el código más claro, especialmente cuando se manejan errores. No he usado uno en 20 años.

Tony

Siempre traté de mantenerme alejado de las declaraciones de goto (siempre fue menospreciado en la escuela y mi trabajo por alguna razón). Usaría algo como lo que Daemin sugirió.

Puede usar etiquetas, algo así como:

Outer:
for(...)
{
    Inner:
    for(...)
    {
    if(condition)
        {
        goto End;
        }
    }
}
End:

En Java, ¿puedes pasar etiquetas para romper, creo?

Editar - Cambió el goto a Fin en lugar de Exterior, sin embargo, no creo que el representante negativo esté justificado. Esta respuesta ofrece la forma más sencilla de hacerlo.

Otra razón para reconsiderar la construcción for por completo es que su alcance impide el acceso a las variables controladas después de que el ciclo ha terminado. Los valores de las variables que se mutan en el bucle pueden ser útiles por una variedad de razones (por ejemplo, para distinguir el éxito del fracaso en una búsqueda) que de lo contrario requerirían variables adicionales para preservar esa información después de la salida del alcance. Aquí hay un pequeño ejemplo que busca en una matriz cuadrada llamada a un valor target (suponiendo que SIZE no es cero, de lo contrario no es necesaria la búsqueda):

int i = 0;
int j = 0;
while (i < SIZE && a[i][j] != target) { // still in array but not at target
    if (SIZE <= ++j) {                  // fallen off the end of a row
        j = 0;
        ++i;
    }
}

El código posterior puede usar i < SIZE para determinar si se encontró el valor deseado.

Otra ventaja de lo anterior es la flexibilidad. Supongamos que ahora estamos informados de que los valores en las filas de <=> son ascendentes, por lo que el resto de una fila es irrelevante si se encuentra un valor mayor que <=>. Es fácil saber exactamente qué cambio hacer y dónde hacerlo. Debido a que esa nueva información nos permite abandonar la fila actual, solo la decisión interna se ve afectada, convirtiéndose en:

    if (target < a[i][j] || SIZE <= ++j) { // can't be in row or fallen off end
    ...

Estoy viendo más de los lenguajes más nuevos (especialmente los orientados funcionalmente) abandonando el viejo " contando " construcción de bucle; probablemente sea algo bueno, ya que nos anima a pensar en el significado del bucle, en lugar de simplemente contar.

La mejor manera que he visto involucra macros y gotos, pero en realidad es bastante agradable (el enlace a la publicación comienza hablando de Perl, pero el último párrafo presenta las macros).

Te permite escribir código como:

named (LOOPS) for (i=1; i<10; i++) {
    for (j=1; j<10; j++) {
        for (j=1; j<10; j++) {
            /* Process data[i][j][k] here */
            if (data[i][j][k] < threshold) break(LOOPS);
        }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top