A la recursividad se le suele poner una etiqueta injusta: "o lo pillas o no lo pillas". No es verdad. Lo que ocurre es que casi siempre se explica con el ejemplo equivocado (el factorial, sin más contexto) y sin mostrar qué está pasando realmente detrás, en la memoria del programa. Una vez ves eso, deja de parecer magia.

En esencia, una función recursiva es simplemente una función que se llama a sí misma para resolver una versión más pequeña del mismo problema, hasta llegar a un caso tan simple que puede resolverse directamente.

Los dos ingredientes de toda función recursiva

Toda función recursiva correcta necesita exactamente dos piezas:

Si falta el caso base, o si el caso recursivo no se acerca nunca a él, la función se llama a sí misma indefinidamente hasta que el programa se queda sin memoria para seguir. Eso es justo lo que veremos con la pila de llamadas.

Qué es la pila de llamadas

Cada vez que una función llama a otra (o a sí misma), el ordenador guarda en la pila de llamadas (call stack) el punto exacto donde debe volver y con qué valores, y "apila" esa nueva llamada encima. Cuando esa llamada termina, se "desapila" y el control vuelve al punto guardado.

En una función recursiva, cada llamada a sí misma añade un nuevo nivel a la pila. Si el caso base nunca llega, la pila crece sin parar hasta agotar la memoria reservada para ella, lo que produce el clásico error RecursionError en Python o Maximum call stack size exceeded en JavaScript.

python
def factorial(n):
    if n <= 1:          # caso base: detiene la recursión
        return 1
    return n * factorial(n - 1)  # caso recursivo: problema más pequeño

print(factorial(5))  # 5 * 4 * 3 * 2 * 1 = 120

# Pila de llamadas al calcular factorial(5):
# factorial(5) -> factorial(4) -> factorial(3) -> factorial(2) -> factorial(1)
# factorial(1) devuelve 1, y cada nivel multiplica y "desapila" hacia arriba

Fibonacci: cuando la recursión se repite de más

La sucesión de Fibonacci (cada número es la suma de los dos anteriores: 0, 1, 1, 2, 3, 5, 8...) es el ejemplo perfecto para entender un problema real de la recursividad ingenua: la repetición de trabajo.

python
def fibonacci(n):
    if n <= 1:
        return n  # caso base
    return fibonacci(n - 1) + fibonacci(n - 2)  # dos llamadas recursivas

print(fibonacci(6))  # 8

El problema: fibonacci(6) vuelve a calcular fibonacci(4) dos veces, fibonacci(3) tres veces, y así sucesivamente. Esta versión tiene una complejidad exponencial, muy lejos de ser eficiente. La solución habitual es "memoizar" (guardar en caché los resultados ya calculados) o pasarse a una versión iterativa, algo a tener en cuenta si te interesa cómo se comparan distintos algoritmos según su eficiencia.

Recorrer un árbol: donde la recursividad brilla de verdad

Hay problemas donde la recursividad no es solo una opción elegante, sino la forma más natural de resolverlos: recorrer estructuras jerárquicas como árboles (el DOM de una web, un sistema de archivos, un árbol genealógico). Cada nodo puede tener hijos que son, a su vez, árboles más pequeños con la misma forma.

python
def contar_nodos(arbol):
    if arbol is None:
        return 0  # caso base: un árbol vacío no tiene nodos
    total = 1  # cuenta el nodo actual
    for hijo in arbol.get("hijos", []):
        total += contar_nodos(hijo)  # recursión sobre cada subárbol
    return total

carpeta = {
    "nombre": "proyecto",
    "hijos": [
        {"nombre": "src", "hijos": [{"nombre": "main.py"}]},
        {"nombre": "README.md"},
    ],
}
print(contar_nodos(carpeta))  # 4

Escribir este mismo recorrido de forma iterativa es posible, pero requiere gestionar tú mismo una pila o cola manual: la recursividad, en este caso, aprovecha directamente la pila de llamadas que ya te da el lenguaje. Este mismo patrón aparece cuando se explica cómo funciona un array frente a una lista enlazada, donde recorrer los nodos también puede plantearse de forma recursiva.

¿Recursividad o bucle? Cómo decidir

Como regla práctica: si el problema tiene una estructura naturalmente jerárquica o "problema grande hecho de problemas más pequeños idénticos" (árboles, backtracking, divide y vencerás), la recursividad suele dar un código más claro. Si el problema es simplemente "repite esto N veces", un bucle normal es casi siempre más legible y más eficiente en memoria, porque no acumula llamadas en la pila. La recursividad no es "más avanzada" que un bucle; es una herramienta distinta para un tipo de problema distinto.

Ponte a prueba

Ponte a prueba

1. ¿Qué es el "caso base" de una función recursiva?

2. ¿Qué estructura usa el ordenador para gestionar las llamadas recursivas?

3. ¿Qué ocurre si una función recursiva nunca alcanza su caso base?

4. ¿Qué tipo de problema encaja mejor con recursividad de forma natural?

5. La versión ingenua de Fibonacci recursivo es lenta principalmente porque...

JR

Javier Rodríguez Fernández

Programador con Grado Superior en Desarrollo de Aplicaciones Multiplataforma (DAM). Escribe en DevPulso sobre programación aplicada, sin relleno.