La ricorsione è un concetto potente della programmazione: una funzione che chiama se stessa. All'inizio può essere un po' difficile da afferrare, ma una volta che ne capisci i fondamenti diventa uno strumento prezioso per risolvere problemi complessi. In questo tutorial esploreremo la ricorsione in JavaScript con esempi facili da capire.
La ricorsione si ha quando una funzione chiama se stessa, in modo diretto o indiretto. È simile a un ciclo, ma consiste nel suddividere un problema in sotto-problemi più piccoli e più gestibili.
Iniziamo con un esempio semplice: una funzione di conto alla rovescia.
function countdown(num) {
// Base case
if (num <= 0) {
console.log('Blastoff!');
return;
}
// Recursive case
console.log(num);
countdown(num - 1);
}
// Call the function
countdown(5);
In questo esempio:
num diventa minore o uguale a 0, la funzione stampa "Blastoff!" e smette di chiamare se stessa.num e chiama se stessa con num - 1.Ora vediamo un esempio classico di ricorsione: il calcolo del fattoriale di un numero.
function factorial(n) {
// Base case
if (n === 0 || n === 1) {
return 1;
}
// Recursive case
return n * factorial(n - 1);
}
// Test the function
console.log(factorial(5)); // Output: 120
In questo esempio:
n è 0 o 1, la funzione restituisce 1.n per il fattoriale di n - 1.Ogni funzione ricorsiva deve avere almeno un caso base, una condizione in cui la funzione smette di chiamare se stessa. Senza un caso base, la ricorsione continuerebbe all'infinito, causando uno stack overflow.
Il caso ricorsivo definisce come la funzione chiama se stessa con una versione più piccola o più semplice del problema.
Vantaggi:
Svantaggi:
La ricorsione è una tecnica preziosa: può semplificare problemi complessi suddividendoli in sotto-problemi più piccoli e più gestibili. Capire i casi base e i casi ricorsivi è fondamentale per scrivere soluzioni ricorsive efficaci in JavaScript.
Per saperne di più:
Gestisci una pizzeria e offri tre tipi di pizza:
Se i clienti lo desiderano, possono aggiungere un numero illimitato di opzioni extra: "ExtraSauce" per $1 oppure "ExtraToppings" per $2.
Il tuo compito è scrivere del codice che aiuti il cliente a capire quanto spenderà.
Ricevendo il nome della pizza come primo argomento e un numero illimitato di opzioni extra, calcola il prezzo della pizza in dollari.
pizzaPrice('Margherita');
// => 7
pizzaPrice('Caprese', 'ExtraSauce', 'ExtraToppings');
// => 12
pizzaPrice(
'Caprese',
'ExtraToppings',
'ExtraToppings',
'ExtraToppings',
'ExtraToppings',
);
// => 17
La funzione viene chiamata con un array di PizzaOrder e deve restituire il prezzo totale dell'ordine in dollari.
Ogni PizzaOrder ha una proprietà pizza, il nome della pizza, e una proprietà extras, l'array delle opzioni extra.
const margherita = new PizzaOrder('Margherita');
const caprese = new PizzaOrder('Caprese', 'ExtraToppings');
orderPrice([margherita, caprese]);
// => 18
Ti accorgerai che non puoi scrivere questa funzione usando la ricorsione: un test con una quantità enorme di ordini farà scattare un Maximum call stack size exceeded.
Non preoccuparti, è intenzionale: prova a implementare questa funzione con un ciclo imperativo!
Hai molte opzioni: puoi usare, tra le altre cose, reduce o un ciclo for.
Quando l'interprete JavaScript esegue il codice JavaScript, tiene traccia delle funzioni in cui è entrato (che ha iniziato a chiamare) su una struttura dati chiamata «stack». Quando la funzione restituisce un valore (cioè termina), viene rimossa dallo stack.
Però questo stack ha una dimensione limitata. L'errore più comune è una funzione ricorsiva che non termina mai. Ogni chiamata viene messa sullo stack, ma prima che restituisca, un'altra chiamata viene messa sullo stack.
function kaboom() {
kaboom()
}
kaboom()
// => RangeError: Maximum call stack size exceeded
La traccia dello stack di questo errore mostra sempre la stessa riga, il che ha senso, perché la funzione chiama se stessa. Anche se nella maggior parte dei casi non ha una vera utilità pratica, puoi scoprire quanto può diventare alto quello stack.
let calls = 0;
function kaboom() {
calls +=1 ;
kaboom()
}
kaboom()
// => RangeError: Maximum call stack size exceeded
console.log(calls)
// => a number, generally higher than 10.000
Ci sono solo due soluzioni valide a un errore di stack causato da una funzione ricorsiva sincrona:
Iscriviti a Exercism per imparare e padroneggiare JavaScript con 37 concetti159 esercizi e il mentoring di persone reali, tutto gratis.