Control de flujo de Java: romper y continuar declaraciones

Las declaraciones condicionales y los bucles son una herramienta muy importante en la programación. No hay muchas cosas que podamos hacer con un código que solo puede ejecutarse línea por línea...

Introducción

Las declaraciones condicionales y los bucles son una herramienta muy importante en la programación. No hay muchas cosas que podamos hacer con un código que solo puede ejecutarse línea por línea.

Eso es lo que significa "control de flujo": guiar la ejecución de nuestro programa, en lugar de dejar que se ejecute línea por línea, independientemente de cualquier factor interno o externo. Cada lenguaje de programación admite alguna forma de control de flujo, si no explícitamente a través de ifs y fors o declaraciones similares, implícitamente nos brinda las herramientas para crear tales construcciones, es decir, los lenguajes de programación de bajo nivel generalmente logran ese efecto con una gran cantidad de comandos go-to.

Los bucles eran un concepto utilizado mucho antes de que existiera la programación informática, pero la primera persona en utilizar un bucle de software fue Ada Lovelace, comúnmente conocida por su apellido de soltera - Byron, mientras calculaba [Números de Bernoulli](https://en .wikipedia.org/wiki/Bernoulli_number), allá por el siglo XIX.

En Java, hay varias formas de controlar el flujo del código:

descanso

La declaración break es una de las "sentencias de salto" de Java, ya que transfiere la ejecución del código a otra parte del código. Ya hemos visto la palabra clave break utilizada en la declaración de cambio. Lo revisaremos aquí, junto con otros casos de uso:

  • Se puede usar para detener la ejecución de un caso de declaración switch, en lugar de dejar que continúe ejecutando el código para los siguientes casos también
  • Se puede usar para salir de un bucle antes de que haya terminado todas sus iteraciones, o como una forma de salir de bucles infinitos creados a propósito.
  • Se puede usar como una forma aceptable de la declaración "go-to" cuando tenemos múltiples bucles anidados

Los dos primeros son relativamente similares, ya que ambos se utilizan para terminar prematuramente la ejecución de uno o más bloques de código:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Finding at which index element a is in an array
int[] arr = {1,2,3,4,5,6};
int foundAt = -1;

int a = 4;

for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
    if (arr[i] == a) {
        foundAt = i;
        break;
    }
}
if (foundAt != -1)
    System.out.println(a + " was found at index " + foundAt);
else System.out.println(a + " wasn't found in the array");

Producción:

1
2
3
4
5
1
2
3
4
4 was found at index 3

Como podemos ver, el ciclo for se ejecutó como de costumbre hasta que se encontró con la instrucción break, momento en el que Java dejó de ejecutar el bucle y continuó la ejecución en la primera línea después del bucle for.

Aquí hay otro ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Scanner s = new Scanner(System.in);

while (true) {
    System.out.println("Do you wish to exit the infinite loop? Y/N");
    if (s.hasNext()) {
        String answer = s.next();
        if (answer.equals("Y") || answer.equals("y"))
            break;
    }
}

Este ciclo seguirá preguntando al usuario si desea salir del ciclo hasta que responda con los caracteres apropiados, donde salimos del ciclo usando break, o terminamos el programa con Ctrl + C.

De forma predeterminada, la instrucción break solo sale del bucle más interno en el que se encuentra.

Si quisiéramos encontrar la primera posición en la que se puede encontrar un determinado elemento en una matriz, y quisiéramos salir de los bucles tan pronto como lo encontráramos (similar al ejemplo con una matriz anterior), escribir el siguiente no funcionaría:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int[][] matrix = {{1,2,3},{4,5,6},{7,8,9}};

int foundAtI = -1;
int foundAtJ = -1;

int a = 4;

for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[i].length; j++) {
        System.out.print(matrix[i][j] + " ");
        if (matrix[i][j] == a) {
            foundAtI = i;
            foundAtJ = j;
            break;
        }
    }
    System.out.println();
}

System.out.println();
if (foundAtI != -1)
    System.out.println(a + " was found at indices [" + foundAtI + "," + foundAtJ + "]");
else System.out.println(a + " wasn't found in the matrix");

Producción:

1
2
3
4
1 2 3 
4 
7 8 9 
4 was found at indices [1,0]

Podemos ver que el bucle continuó ejecutándose incluso después de encontrar 4 en la matriz, después de tartamudear en la fila en la que se encontraba 4. Esto se debe a que break solo salió del bucle más interno, es decir, dejó de iterar a través de la fila actual y saltó a la siguiente. Además, nuestra tarea era encontrar la primera aparición de 4 en la matriz, y de esta manera devolveríamos la última aparición de 4 en nuestra matriz.

Aquí es donde entran en juego las declaraciones etiquetadas, que veremos a continuación.

Declaraciones etiquetadas

Las declaraciones etiquetadas se pueden usar junto con las declaraciones break o continue para simular un go-to.

Las etiquetas se pueden aplicar a cualquier bloque de código entre declaraciones {}, for, for-each, while, do-while, if y switch, así como expresiones, asignaciones, Declaraciones return, bloques try y declaraciones throw. Luego los usamos con las declaraciones break y continue.

Las etiquetas funcionan aplicando un identificador a una construcción:

1
Identifier : Construct

Como:

1
2
3
4
someLoop: for (int i = 0; i < 100; i++) {}
someLabel: {
    int i = 10;
}

Entonces podemos invocar estas declaraciones etiquetadas a través de un ‘pausa’ o ‘continuar’. Por ejemplo, aquí hemos etiquetado nuestro bucle externo simplemente como “externo”. Para salir de dos o más bucles anidados, rompemos el bucle exterior llamando a la etiqueta:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
outer: for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[i].length; j++) {
        System.out.print(matrix[i][j] + " ");
        if (matrix[i][j] == a) {
            foundAtI = i;
            foundAtJ = j;
            break outer;
        }
    }
    System.out.println();
}

El uso de este bucle etiquetado en lugar del bucle del ejemplo anterior produce el resultado y el comportamiento correctos:

1
2
3
1 2 3
4
4 was found at indices [1,0]

No puede usar break label si el break no está ubicado dentro de un bloque de código designado por esa etiqueta o causará un error durante la compilación.

Seguir

La instrucción continuar simplemente salta el resto de la iteración actual y continúa con la siguiente. Es útil cuando queremos omitir iteraciones completas (o parte de ellas) que cumplen ciertas condiciones, especialmente si tienen un alto rendimiento.

Para un bucle while, "saltar" el resto de la iteración significa volver directamente a verificar la condición antes de la siguiente iteración, y para un bucle for esto significa ir a la parte "paso" del bucle for (la parte en la que generalmente incrementamos/decrementamos la variable de control) y luego comprobamos la condición antes de la siguiente iteración.

continuar generalmente se usa con una declaración si -> continuaremos si se cumple una determinada condición. Usamos este ‘si’ para ejecutar el resto de la iteración solo si no se cumple la condición, y omitiremos el uso de ‘continuar’. Por ejemplo

1
2
3
4
5
6
7
// We want to print every number from 1 to 20, except those divisible by 3
for (int i = 1; i <= 20; i++) {
    if (i % 3 == 0)
        continue;
    
    System.out.println(i);
}

El continuar en nuestro ejemplo podría evitarse fácilmente mediante el uso de una declaración si ligeramente modificada, principalmente imprimiendo el número si i % 3 != 0 es verdadero, de lo contrario no se hace nada.

El consejo general con respecto a “romper” y (especialmente) “continuar” es tratar de usarlos principalmente al comienzo de las iteraciones como algún tipo de condición previa que estamos verificando. Usar un ‘continuar’ en algún lugar en medio de la iteración es una excelente manera de causar errores que te llevará un tiempo descubrir.

Por supuesto, también puede usar continuar con declaraciones etiquetadas:

1
2
3
4
5
6
7
8
start: for (int i = 0; i < 10; i++) {
    System.out.println();
    for (int j = 0; j < 10; j++) {
        if (j >= i)
            continue start;
    }
    System.out.println("Since j will always be equal to or more than i, the 'start' loop will continue running indefinitely, and this piece of code will never run.");
}

Conclusión

El control de flujo en el código es esencial en casi todas las aplicaciones. Las declaraciones que alteran el flujo del código son bloques de construcción fundamentales y cada aspirante a desarrollador debe tener el control completo y ser consciente de cómo funcionan.

Utilizando las declaraciones break y continue, los desarrolladores de Java pueden simular declaraciones go-to y salir de ciertos bucles si es necesario. o.

Licensed under CC BY-NC-SA 4.0