Estructuras de control
Las estructuras de control determinan el flujo de ejecución de un programa. Se clasifican en tres grandes familias: secuenciales (las instrucciones se ejecutan una tras otra), condicionales o de selección (se ejecuta un bloque u otro según una condición) y repetitivas o iterativas (un bloque se repite mientras se cumpla una condición). A partir de estas, junto con la recursividad, se construye cualquier algoritmo.
Estructuras condicionales (selección)
La selección permite ejecutar código solo cuando se cumple una condición lógica.
-
En Python, la palabra clave para definir una condición es
if; para especificar la acción alternativa cuando la condición no se cumple se usaelse(yelifpara condiciones encadenadas). Así,ifselecciona el bloque cuando la expresión es verdadera yelsecuando es falsa. -
La estructura
switch-case(en C, C++, Java) compara una expresión contra varios valores (case). Sobre ella conviene precisar: la cláusuladefaultNO es obligatoria; es opcional y solo se ejecuta cuando ningúncasecoincide. Por tanto, es falso afirmar que "es obligatorio meter siempre la opcióndefault, si no el bucle no funcionaría" (ademásswitchno es un bucle, sino una selección múltiple).
Estructuras repetitivas (bucles)
Un bucle repite un bloque de sentencias mientras su expresión de control sea verdadera. Es imprescindible que dentro del bloque existan sentencias que modifiquen la evaluación de la expresión (por ejemplo, incrementar un contador); de lo contrario, la condición nunca cambia y se produce un bucle infinito. Una solución iterativa termina cuando se incumple la condición de continuación del bucle.
Bucle while
Evalúa la expresión antes de ejecutar el bloque. Por tanto:
-
Primero evalúa la expresión; si es verdadera, ejecuta el bloque y vuelve a evaluar.
-
Si la expresión es
false, se sale del bucle (puede no ejecutarse ninguna vez).
Ejemplo de bucle infinito: integer x=0; while x<100 do (x=x+1; print x; x=x-1;) es un bucle infinito, porque x se incrementa y decrementa en cada iteración y su valor final vuelve a 0, de modo que la condición x<100 nunca deja de cumplirse.
Bucle do-while
Evalúa la expresión después de ejecutar el bloque. Su sintaxis es:
Su característica esencial es que se ejecuta al menos una vez antes de comprobar la condición.
La referencia de JavaScript de MDN lo define así: la sentencia do...while «crea un bucle que ejecuta una sentencia especificada, hasta que la condición de comprobación se evalúa como falsa» (en inglés: «creates a loop that executes a specified statement as long as the test condition evaluates to true»). Esa sentencia puede ser una sola o un bloque: «para ejecutar múltiples sentencias dentro de un bucle, utilice la sentencia block ( { ... } ) para agrupar aquellas sentencias». Y la condición es «una expresión [que] se evalúa después de cada pase del bucle». Por eso es correcto decir que el bucle ejecuta «una sentencia», aunque esa sentencia sea un bloque con varias. Por eso, tanto en Java como en C++, la estructura de control que siempre se ejecuta como mínimo una vez es do... while. La diferencia clave entre do-while y while es que do-while evalúa la expresión después de ejecutar el bloque, mientras que while la evalúa antes.
Bucle for
Se usa para iterar un número fijo de veces. Consta de tres partes: inicialización, condición y actualización. La variable que cuenta las iteraciones se denomina variable de control (o contador). En C, C++ y Java, los tres elementos del for son opcionales: for(;;) es válido y produce un bucle infinito.
Traza de ejemplo (Java):
System.out.println(i);
j++;
}
| Iteración | i (impreso) | j | siguiente i = i - j |
|---|---|---|---|
| 1 | 10 | 1→2 | 10-2 = 8 |
| 2 | 8 | 2→3 | 8-3 = 5 |
| 3 | 5 | 3→4 | 5-4 = 1 |
| 4 | 1 | 4→5 | 1-5 = -4 (fin) |
Se imprime 4 veces y la salida es 10 8 5 1 (10851).
Instrucciones break y continue
Dentro de un bucle: break detiene (termina) el bucle por completo y cede el control a la instrucción siguiente; continue salta a la siguiente iteración, omitiendo el resto del bloque en esa vuelta. En C++, la forma de salir de un bucle es mediante la instrucción break.
Variable centinela
Una variable centinela es una variable que se usa para indicar el final de un proceso o de una secuencia. Permite terminar un bucle cuando se detecta un valor especial (el centinela) en lugar de un contador fijo.
Recursividad
La recursividad es una técnica de programación en la que una función se llama a sí misma en su propia definición. Un problema recursivo se divide en instancias más pequeñas del mismo problema; la inducción en la recursividad consiste en suponer que las instancias más pequeñas quedan resueltas para construir, a partir de ellas, la solución del caso general.
Toda función recursiva necesita:
-
Un caso base (o condición de salida): es esencial para evitar un bucle/llamada infinita y detener la recursión.
-
Un paso recursivo que reduzca el problema hacia el caso base.
Ejemplo de función recursiva en Java:
else return n + suma(n - 1);
}
Recursividad y pila de llamadas
En cada llamada recursiva se reserva un marco (frame) en la pila, donde se almacena la información imprescindible para continuar la ejecución: los parámetros y variables locales, la dirección de retorno y el estado de la llamada. NO es imprescindible almacenar el número de orden de la iteración (ese es un concepto propio de la iteración, no de la recursión). En la búsqueda binaria, la implementación recursiva es menos eficiente en memoria que la iterativa porque requiere almacenar las variables en cada llamada apiladas.
Recursividad frente a iteración
| Aspecto | Recursividad | Iteración |
|---|---|---|
| Mecanismo | Una función se llama a sí misma | Se repite un bucle |
| Ventaja | Código más simple y legible en problemas descomponibles | Menor consumo de memoria |
| Desventaja | Puede causar desbordamiento de pila (stack overflow) en gran profundidad | Puede resultar más compleja de expresar |
| Riesgo común | Ambas pueden entrar en un ciclo infinito | — |
La principal diferencia es que la recursividad utiliza funciones que se llaman a sí mismas, mientras que la iteración utiliza bucles. La recursividad suele ser preferible en problemas que pueden descomponerse en subproblemas similares y más pequeños; es la base de los algoritmos de divide y vencerás (división y conquista), cuyo objetivo es simplificar un problema complejo en subproblemas más pequeños mediante recursión.
Fuentes
-
Oracle, The Java Tutorials — The while and do-while Statements: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/while.html
-
Oracle, The Java Tutorials — The for Statement: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html
-
Oracle, The Java Tutorials — Branching Statements (break, continue): https://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
-
Python Software Foundation, The Python Language Reference — Compound statements (if / elif / else): https://docs.python.org/3/reference/compound_stmts.html
-
cppreference, do-while loop (C++): https://en.cppreference.com/w/cpp/language/do
-
MDN Web Docs, do...while (referencia de JavaScript): https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Statements/do...while · en inglés: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/do...while
-
cppreference, for loop (C++): https://en.cppreference.com/w/cpp/language/for
-
cppreference, switch statement (C++): https://en.cppreference.com/w/cpp/language/switch