Funcția se numește singură până când cineva o oprește.

Recursivitatea poate fi dificilă pentru noii dezvoltatori. Poate asta pentru că multe resurse îl învață folosind exemple algoritmice (Fibonacci, link-lists). Această piesă va introduce, sperăm, lucrurile clar, folosind un exemplu simplu.

Ideea de bază

Recursivitate este atunci când o funcție se apelează până când cineva o oprește. Dacă nimeni nu o oprește, atunci o va face recurge (se numește) pentru totdeauna.

nu-asta-este-Patrick

Funcțiile recursive vă permit să efectuați o unitate de lucru de mai multe ori. Exact asta este for/while buclele să ne realizăm! Uneori, însă, soluțiile recursive reprezintă o abordare mai elegantă pentru rezolvarea unei probleme.

Funcția de numărătoare inversă

Să creăm o funcție care numără înapoi de la un număr dat. O vom folosi așa.

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

Și iată algoritmul nostru pentru a rezolva această problemă.

  1. Luați un parametru numit number. Acesta este punctul nostru de plecare.
  2. Pleacă de la number până la 0, înregistrând fiecare pe parcurs.

Vom începe cu un for buclă și apoi comparați-o cu una recursivă.

Abordare imperativă (bucle)

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

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

Acesta conține ambii pași algoritmici.

  1. ✅ Luați un parametru numit number.
  2. ✅ Înregistrați totul de la number la 0.

Abordare recursivă

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

    console.log(number);    
    countDownFrom(number - 1);
}

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

Acesta trece și el.

  1. ✅ Luați un parametru numit number.
  2. ✅ Înregistrați totul de la number la 0.

Deci, conceptual, cele două abordări sunt aceleași. Cu toate acestea, ei își fac treaba în moduri diferite.

Depanarea soluției noastre imperative

Pentru un exemplu mai vizual, să punem un debugger în versiunea noastră de buclă și aruncați-o în Chrome Developer Tools.

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

countdownFrom-iterative

Vedeți cum folosește o variabilă suplimentară, i, pentru a urmări numărul curent? Pe măsură ce iterați i scade, lovind în cele din urmă 0 și se termină.

Și în for bucla am specificat „opriți dacă i > 0“.

Depanarea soluției noastre recursive

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

    console.log(number);
	
	debugger;

    countDownFrom(number - 1);
}

numărătoarea inversă De la recursiv

Versiunea recursivă nu are nevoie de variabile suplimentare pentru a-i urmări progresul. Observați cum teancul de funcții (stivă de apeluri) crește pe măsură ce recidivăm?

Asta pentru că fiecare apel la countDownFrom adaugă la stivă, hrănindu-l number - 1. Făcând asta, trecem de-a lungul unei actualizări number de fiecare data. Nu este nevoie de o stare suplimentară!

Aceasta este principala diferență între cele două abordări.

  1. Iterativă folosește starea internă (variabile suplimentare pentru numărare etc.).
  2. Recursiv nu, pur și simplu trece parametrii actualizați între fiecare apel.

Dar cum știe oricare dintre versiuni când să se oprească?

Bucle Infinite

În călătoriile dvs., este posibil să fi fost avertizat cu privire la temuta buclă infinită.

? 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?!') }

Deoarece teoretic vor rula pentru totdeauna, o buclă infinită vă va opri programul și, eventual, vă va bloca browserul. Le puteți preveni codificând întotdeauna un starea de oprire.

✅ 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); }

În ambele cazuri ne logăm x, creșteți-l și opriți-vă când devine 3. Al nostru countDownFrom funcția avea o logică similară.

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

Din nou, buclele au nevoie de o stare suplimentară pentru a determina când ar trebui să se oprească. Asta e ceea ce x și i sunt pentru.

Recursivitate infinită

Recursivitatea prezintă, de asemenea, același pericol. Nu este greu să scrieți o funcție de auto-referință care să vă blocheze browserul.

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

run();
// running
// running
// ...

este-acesta-este-recursiv

Fără o condiție de oprire, run se va numi pentru totdeauna. Puteți repara asta cu un if afirmație.

✅ 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.

Caz de baza

Acest lucru este cunoscut sub numele de caz de baza–Recursivul nostru countDownFrom a avut unul.

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

Este aceeași idee ca logica de oprire a buclei noastre. Indiferent de abordarea pe care o alegeți, amintiți-vă întotdeauna asta la un moment dat trebuie oprit.

este-asta-trebuie-să-te-oprești

rezumat

  • Recursivitatea este atunci când o funcție se numește până când cineva o oprește.
  • Poate fi folosit în loc de o buclă.
  • Dacă nimeni nu îl oprește, va recidiva pentru totdeauna și va prăbuși programul.
  • A caz de baza este o condiție care oprește recursiunea. Nu uitați să le adăugați!
  • Buclele folosesc variabile de stare suplimentare pentru urmărire și numărare, în timp ce recursivitatea utilizează numai parametrii furnizați.

bucle de dispariție

Mulțumesc pentru lectură

Pentru mai mult conținut de genul acesta, verificați https://yazeedb.com. Și vă rog să-mi spuneți ce altceva ați dori să vedeți! DM-urile mele sunt deschise pe Twitter.

Pana data viitoare!