Una introducción rápida a la recursividad en JavaScript

La función se llama a sí misma hasta que alguien la detiene.

La recursividad puede resultar difícil para los nuevos desarrolladores. Tal vez sea porque muchos recursos lo enseñan usando ejemplos algorítmicos (Fibonacci, listas vinculadas). Con suerte, esta pieza presentará las cosas de manera sencilla, utilizando un ejemplo simple.

Idea principal

La recursividad es cuando una función se llama a sí misma hasta que alguien la detiene. Si nadie lo detiene, se repetirá (se llamará a sí mismo) para siempre.

no-esto-es-patrick

Las funciones recursivas le permiten realizar una unidad de trabajo varias veces. ¡Esto es exactamente lo que los for/whilebucles nos permiten lograr! A veces, sin embargo, las soluciones recursivas son un enfoque más elegante para resolver un problema.

Función de cuenta atrás

Creemos una función que cuenta regresivamente a partir de un número dado. Lo usaremos así.

countDownFrom(5); // 5 // 4 // 3 // 2 // 1 

Y aquí está nuestro algoritmo para resolver este problema.

  1. Tome un parámetro llamado number. Este es nuestro punto de partida.
  2. Vaya de numberabajo a 0, registrando cada uno en el camino.

Comenzaremos con un forenfoque de bucle y luego lo compararemos con uno recursivo.

Enfoque imperativo (bucles)

function countDownFrom(number) { for (let i = number; i > 0; i--) { console.log(i); } } countDownFrom(5); // 5 // 4 // 3 // 2 // 1 

Éste contiene ambos pasos algorítmicos.

  1. ✅ Tome un parámetro llamado number.
  2. ✅ Registre todo desde numberhasta 0.

Enfoque recursivo

function countDownFrom(number) { if (number === 0) { return; } console.log(number); countDownFrom(number - 1); } countDownFrom(5); // 5 // 4 // 3 // 2 // 1 

Este también pasa.

  1. ✅ Tome un parámetro llamado number.
  2. ✅ Registre todo desde numberhasta 0.

Entonces, conceptualmente, los dos enfoques son los mismos. Sin embargo, hacen el trabajo de diferentes maneras.

Depurando nuestra solución imperativa

Para obtener un ejemplo más visual, pongamos un debuggeren nuestra versión de bucle y lo lanzaremos a las herramientas de desarrollo de Chrome.

function countDownFrom(number) { for (let i = number; i > 0; i--) { console.log(i); debugger; } } 

cuenta regresiva

¿Ves cómo usa una variable adicional i, para rastrear el número actual? A medida que itera i, disminuye, eventualmente golpeando 0y terminando.

Y en el forciclo especificamos "detener si i > 0".

Depurando nuestra solución recursiva

function countDownFrom(number) { if (number === 0) { return; } console.log(number); debugger; countDownFrom(number - 1); } 

cuenta regresiva de recursivo

La versión recursiva no necesita variables adicionales para rastrear su progreso. ¿Observa cómo crece la pila de funciones ( pila de llamadas ) a medida que recurrimos?

Eso es porque cada llamada a se countDownFromsuma a la pila, alimentándola number - 1. Al hacer esto, estamos transmitiendo una actualización numbercada vez. ¡No se necesita estado adicional!

Esa es la principal diferencia entre los dos enfoques.

  1. Iterativo utiliza el estado interno (variables adicionales para contar, etc.).
  2. El recursivo no lo hace, simplemente pasa parámetros actualizados entre cada llamada.

Pero, ¿cómo sabe cualquiera de las versiones cuándo detenerse?

Bucles infinitos

En sus viajes, es posible que le hayan advertido sobre el temido bucle infinito.

? THIS RUNS FOREVER, BE WARNED ? while (true) { console.log('WHY DID YOU RUN THIS?!' } ? THIS RUNS FOREVER, BE WARNED ? for (i = 0;;) { console.log('WHY DID YOU RUN THIS?!') } 

Dado que teóricamente se ejecutarían para siempre, un bucle infinito detendrá su programa y posiblemente bloqueará su navegador. Puede prevenirlos codificando siempre una condición de detención .

✅ This does not run forever x = 0; while (x < 3) { console.log(x); x++; } ✅ This does not run forever for (x = 0; x < 3; x++) { console.log(x); } 

En ambos casos, lo registramos x, lo incrementamos y nos detenemos cuando se convierte en 3. Nuestra countDownFromfunción tenía una lógica similar.

// Stop at 0 for (let i = number; i > 0; i--) 

Nuevamente, los bucles necesitan un estado adicional para determinar cuándo deben detenerse. Para eso xy ison.

Recursión infinita

La recursividad también presenta el mismo peligro. No es difícil escribir una función de autorreferencia que bloquee su navegador.

?THIS RUNS FOREVER, BE WARNED? function run() { console.log('running'); run(); } run(); // running // running // ... 

es-esto-un-recursivo

Without a stopping condition, run will forever call itself. You can fix that with an if statement.

✅ This does not run forever function run(x) { if (x === 3) return; console.log('running'); run(x + 1); } run(0); // running // running // running // x is 3 now, we're done. 

Base case

This is known as the base case–our recursive countDownFrom had one.

if (number === 0) { return; } 

It's the same idea as our loop's stopping logic. Whichever approach you pick, always remember that at some point it needs to be stopped.

¿Es esto lo que necesitas?

Summary

  • Recursion is when a function calls itself until someone stops it.
  • It can be used instead of a loop.
  • If no one stops it, it'll recurse forever and crash your program.
  • A base case is a condition that stops the recursion. Don't forget to add them!
  • Loops use extra state variables for tracking and counting, while recursion only uses the provided parameters.

bucles de desaparición

Thanks for reading

Para obtener más contenido como este, visite //yazeedb.com. ¡Y hágame saber qué más le gustaría ver! Mis DM están abiertos en Twitter.

¡Hasta la proxima vez!