Recientemente estoy aprendiendo sobre la recursión debido al algoritmo factorial. No obstante, mi pregunta es:
por ejemplo, haga un bucle 5 veces y luego inserte sus "índices" en una representación de matriz [1,2,3,4,5]
He creado 2 funciones [bucle | recursivo] con cada uno intentando lograr el mismo resultado. La versión "for loop" es la siguiente:
function loop(index, length) { const result = []; for (let i = index; i <= length; i++) result.push(i); return result; } console.log(loop(1, 5)); // [ 1, 2, 3, 4, 5 ]Sin embargo, la versión "recursiva" no imprime el resultado deseado.
function recursive(index, length) { const result = []; result.push(index); return (index < length) ? recursive(++index, length) : result; } console.log(recursive(1, 5)); // [ 5 ]¿Puede alguien amablemente mostrarme por qué no salió [1, 2, 3, 4, 5]?
Porque el result se reinicializa a [] cada vez que se llama a la función. No se está pasando el result anterior, por lo que se reinicia. Al final, solo se devuelve la última iteración con el último índice ( [5] ).
Lo que puede hacer es usar un tercer argumento, opcional, que puede inicializar en [] .
function recursive(index,length, result=[]) { result.push(index); return (index < length) ? recursive(++index,length, result) : result; } console.log(recursive(1,5)); // [1,2,3,4,5]La respuesta de Jeremy Thille es genial. Explica lo que está mal y ofrece una alternativa útil y comprensible. El código es limpio y, como es recursivo en la cola, puede aprovechar la optimización de la cola de llamadas si alguna vez se generaliza. Sin embargo, hay un error: presumiblemente recursive (10, 3) debería devolver una matriz vacía. Aquí volverá [10] .
Si bien podemos solucionarlo fácilmente, veamos una alternativa que es lógicamente más simple y que evita el parámetro predeterminado. (Una respuesta anterior explica algunos de los posibles problemas con los parámetros predeterminados. Comprenda que siguen siendo una técnica útil y poderosa, pero tienen un inconveniente).
Así que aquí hay una versión alternativa, ambas en sintaxis moderna:
const range = (from, to) => from > to ? [] : [from, ... range (from + 1, to)]y en un estilo antiguo:
function range (from, to) { if (from > to) { return [] } return [from] .concat (range (from + 1, to)) } En cualquiera de las versiones, simplemente recurrimos creando una nueva matriz, comenzando con el valor from y luego incluyendo todos los valores de la llamada recursiva. Llegamos a un caso base cuando from es mayor que to y devolvemos una matriz vacía.
Hay una gran posible desventaja eventual en esto. No es recursivo a la cola. Las funciones en las que cualquier llamada recursiva se devuelve inmediatamente sin más procesamiento se denominan recursivas de cola. Hay algunas optimizaciones muy útiles que los motores JS pueden aplicar a las funciones recursivas de cola para evitar el desbordamiento de la pila de recursividad.
Podríamos hacer que esta cola sea recursiva agregando el parámetro predeterminado o agregando una función contenedora que pase ese parámetro a una función auxiliar recursiva interna. Cualquiera de los dos sería fácil de hacer. Sin embargo, podemos ver que hace poca diferencia en JS moderno, ya que en el momento de esta respuesta casi ningún motor admite llamadas de cola adecuadas, aunque creo que hacerlo todavía está en la especificación. Así que por ahora, no nos molestaremos.
Aquí está esta versión en acción:
const range = (from, to) => from > to ? [] : [from, ... range (from + 1, to)] console .log (range (1, 5)) //=> [1, 2, 3, 4, 5] console .log (range (4, 4)) //=> [4] console .log (range (5, 1)) //=> [] .as-console-wrapper {max-height: 100% !important; top: 0}Una solución podría ser agregar un acumulador a su función recursiva, para traer en cada paso la matriz que comenzó al comienzo de la primera llamada.
function recursive(index, length) { const result = []; result.push(index); return recursiveAcc(++index, length, result); } function recursiveAcc(index, length, result) { result.push(index); return (index < length) ? recursiveAcc(++index, length, result) : result; } console.log(recursive(1, 5)); // [ 1, 2, 3, 4, 5 ]