Estructuras de control: tipos, ejemplos y cuándo usar cada una
En este artículo
Las estructuras de control son las instrucciones que determinan en qué orden se ejecuta el código. Sin ellas, un programa solo podría hacer lo mismo siempre, línea tras línea, sin tomar decisiones ni repetir nada.
Existen tres tipos, y con solo esos tres se puede escribir cualquier programa posible. No es una exageración: es un resultado demostrado en teoría de la computación.
Los tres tipos
1. Secuencial
Las instrucciones se ejecutan una detrás de otra, en el orden en que están escritas. Es la estructura por defecto.
precio = 100
impuesto = precio * 0.21
total = precio + impuesto
print(total)
El orden importa: si calculas el total antes del impuesto, el resultado está mal. Parece obvio, pero es el origen de muchos errores en cálculos encadenados.
2. Selectiva (condicionales)
El programa elige un camino según se cumpla o no una condición.
if edad >= 18:
print("Puede acceder")
else:
print("Acceso denegado")
Con varias condiciones encadenadas:
if nota >= 9:
calificacion = "Sobresaliente"
elif nota >= 7:
calificacion = "Notable"
elif nota >= 5:
calificacion = "Aprobado"
else:
calificacion = "Suspenso"
Detalle importante: las condiciones se evalúan en orden y se ejecuta la primera que se cumple. Por eso hay que ir de la más restrictiva a la menos. Si pusieras nota >= 5 primero, un 9 entraría ahí y nunca llegaría a "Sobresaliente".
El switch o match
Cuando comparas una misma variable contra muchos valores, hay una estructura más legible:
// JavaScript
switch (dia) {
case 1: nombre = "Lunes"; break;
case 2: nombre = "Martes"; break;
default: nombre = "Desconocido";
}
En JavaScript y otros lenguajes derivados de C, olvidar el break hace que la ejecución continúe en el caso siguiente. Es un error clásico y difícil de ver.
El operador ternario
Versión compacta para condiciones simples:
estado = "activo" if usuario.conectado else "inactivo"
Úsalo solo cuando quepa cómodamente en una línea. Anidar ternarios produce código ilegible.
3. Repetitiva (bucles)
Repiten un bloque mientras se cumpla una condición.
# while: no sabes cuántas vueltas harán falta
intentos = 0
while not conectado and intentos < 5:
conectar()
intentos += 1
# for: recorres una colección o un número conocido de veces
for producto in carrito:
total += producto.precio
La diferencia entre while y do-while está en cuándo se comprueba la condición: while lo hace antes (puede no ejecutarse nunca) y do-while después (se ejecuta al menos una vez).
Cuál usar en cada caso
| Situación | Estructura |
|---|---|
| Pasos que van siempre en el mismo orden | Secuencial |
| Dos caminos posibles | if / else |
| Varios caminos según rangos | if / elif / else |
| Muchos valores exactos de una variable | switch o match |
| Repetir un número conocido de veces | for |
| Repetir hasta que algo cambie | while |
| Ejecutar al menos una vez y repetir | do-while |
Combinar condiciones
Dentro de un condicional puedes usar operadores lógicos:
if edad >= 18 and tiene_documento and not esta_sancionado:
permitir_entrada()
El orden importa por la evaluación de cortocircuito: si la primera condición de un and es falsa, las siguientes ni se evalúan. Esto se aprovecha para evitar errores:
if usuario is not None and usuario.activo:
...
Si usuario es nulo, la segunda condición no se ejecuta y no se produce el error de acceder a una propiedad inexistente. Invertir el orden rompería el programa.
Controlar el flujo desde dentro
breaksale del bucle inmediatamente.continuesalta a la siguiente vuelta sin ejecutar el resto del bloque.returnsale de la función entera, devolviendo un valor.
for pedido in pedidos:
if pedido.cancelado:
continue # este lo ignoramos
if pedido.id == buscado:
encontrado = pedido
break # ya lo tenemos, salimos
Anidamiento y cómo evitarlo
Las estructuras se pueden meter unas dentro de otras, pero pasado cierto punto el código se vuelve ilegible:
# difícil de seguir
if usuario:
if usuario.activo:
if usuario.tiene_permiso:
if not usuario.bloqueado:
permitir()
La técnica para arreglarlo se llama retorno temprano: descarta los casos negativos primero y deja el caso válido sin anidar.
# mucho más claro
if not usuario: return
if not usuario.activo: return
if not usuario.tiene_permiso: return
if usuario.bloqueado: return
permitir()
Regla práctica: si pasas de tres niveles de anidamiento, hay algo que conviene reorganizar.
Representación en diagramas de flujo
Los símbolos estándar, útiles si estudias algoritmos:
- Óvalo: inicio y fin.
- Rectángulo: proceso o instrucción.
- Rombo: decisión, con dos salidas (sí y no).
- Paralelogramo: entrada o salida de datos.
- Flechas: el sentido del flujo.
Un condicional se dibuja como un rombo que abre dos caminos y vuelve a unirlos. Un bucle es un camino que regresa hacia atrás, al rombo de la condición.
Errores frecuentes
Confundir asignación con comparación. En muchos lenguajes = asigna y == compara. Escribir if (x = 5) asigna el valor y la condición siempre resulta verdadera.
Bucles infinitos. Olvidar actualizar la variable de la condición dentro del bucle.
Orden equivocado en las condiciones. De menos restrictiva a más restrictiva hace que las últimas ramas nunca se alcancen.
Olvidar el break en un switch. La ejecución continúa en los casos siguientes.
Comparar decimales con igualdad exacta. Por la precisión de coma flotante, if (0.1 + 0.2 == 0.3) es falso.
Preguntas frecuentes
¿Cuántas estructuras de control hay?
Tres: secuencial, selectiva y repetitiva. Todo lo demás son variantes de estas.
¿Se puede programar sin condicionales?
En la práctica no. Cualquier programa útil necesita tomar decisiones.
¿Es lo mismo estructura de control que estructura de datos?
No. Las de control determinan el flujo de ejecución; las de datos organizan la información (listas, diccionarios, árboles).
¿Está mal usar break y continue?
No, si mejoran la legibilidad. Abusar de ellos en bucles largos sí dificulta seguir el flujo.
¿Aprendiendo a programar? En nuestro catálogo de cursos técnicos tienes formación desde cero, con acceso inmediato y de por vida.