Recursion — o'z-o'zini chaqirish
Muammodan boshlaylik
Papka ichida papkalar bor, ular ichida yana papkalar. Necha qavat chuqur ekanini bilmaysiz. Barcha fayllarni topish kerak.
Sikl bilan yozish qiyin — necha marta ichiga kirish kerakligini bilmaymiz. Recursion bu muammoni tabiiy hal qiladi.
Recursion nima?
Recursion — funksiyaning o'z-o'zini chaqirishi. Muammo kichik qismlarga bo'linadi — har bir qism xuddi shu funksiya bilan hal qilinadi.
Har bir rekursiv funksiyada ikkita narsa bo'lishi shart:
- Base case — to'xtash sharti. Bo'lmasa — cheksiz chaqiruv.
- Recursive case — o'zini kichikroq muammo bilan chaqirish.
function sanash(n) {
if (n <= 0) return; // ← base case: to'xtash
console.log(n);
sanash(n - 1); // ← recursive case: kichikroq muammo
}
sanash(3);
// 3
// 2
// 1
Hayotiy o'xshatma
Oynalar zali — ikki tomonda katta oynalar. Oynaga qaraganda — o'zingizni ko'rasiz, lekin orqangizda yana o'zingiz, undan keyin yana... Bu cheksiz davom etadi. Recursion ham shunday — funksiya o'zini ko'radi, lekin har safar kichikroq.
Farqi — rekursiv funksiyada to'xtash sharti bor. Oynalar zali esa cheksiz — "stack overflow".
Klassik misol — faktorial
5! = 5 × 4 × 3 × 2 × 1 = 120
Rekursiv ta'rif: n! = n × (n-1)!
function faktorial(n) {
if (n <= 1) return 1; // ← base case
return n * faktorial(n - 1); // ← recursive case
}
faktorial(5);
// 5 * faktorial(4)
// 5 * 4 * faktorial(3)
// 5 * 4 * 3 * faktorial(2)
// 5 * 4 * 3 * 2 * faktorial(1)
// 5 * 4 * 3 * 2 * 1 = 120
Nima uchun shunday?
Nima uchun base case shart?
Base case bo'lmasa — funksiya cheksiz o'zini chaqiradi. Call stack to'lib ketadi:
function cheksiz(n) {
return cheksiz(n - 1); // ← base case yo'q!
}
cheksiz(5); // ❌ RangeError: Maximum call stack size exceeded
Call stack — funksiya chaqiruvlari saqlanadigan joy. Har chaqiruv joy oladi. Cheksiz chaqiruv — joy tugaydi.
Recursion vs Sikl — qachon qaysi?
Sikl — oddiy takrorlash uchun tezroq va xotira tejamkor. Recursion — daraxt tuzilishi (papkalar, DOM, JSON) bilan ishlashda tabiiyroq.
// Faktorial — siklda ham oson
function faktorial(n) {
let natija = 1;
for (let i = 2; i <= n; i++) natija *= i;
return natija;
}
// Bu holda sikl yaxshiroq — oddiyroq, tezroq
Daraxt tuzilishi — recursion kuchi
Ichma-ich ob'ektni tekislash (flatten):
const daraxt = {
qiymat: 1,
bolalar: [
{ qiymat: 2, bolalar: [] },
{
qiymat: 3,
bolalar: [
{ qiymat: 4, bolalar: [] },
{ qiymat: 5, bolalar: [] },
]
}
]
};
function qiymatlarni_ol(tugun) {
const natija = [tugun.qiymat]; // ← joriy tugun qiymati
for (const bola of tugun.bolalar) {
natija.push(...qiymatlarni_ol(bola)); // ← har bola uchun rekursiya
}
return natija;
}
qiymatlarni_ol(daraxt); // [1, 2, 3, 4, 5]
Sikllar bilan bu kodni yozish ancha murakkab bo'lardi.
Fibonacci
0, 1, 1, 2, 3, 5, 8, 13, 21... — har son oldingi ikkitasining yig'indisi.
function fibonacci(n) {
if (n <= 1) return n; // ← base case
return fibonacci(n - 1) + fibonacci(n - 2); // ← recursive case
}
fibonacci(6); // 8
Lekin bu juda sekin — bir xil qiymatlar qayta-qayta hisoblanadi. fibonacci(30) — millionlab chaqiruv.
Yechim — memoization:
function fibonacci(n, kesh = {}) {
if (n <= 1) return n;
if (kesh[n]) return kesh[n]; // ← keshdan qaytaradi
kesh[n] = fibonacci(n - 1, kesh) + fibonacci(n - 2, kesh); // ← keshga saqlaydi
return kesh[n];
}
fibonacci(50); // tez ishlaydi
Tez-tez uchraydigan xatolar
Base case yo'q yoki noto'g'ri:
function sanash(n) {
console.log(n);
sanash(n - 1); // ❌ base case yo'q — cheksiz
}
function sanash(n) {
if (n === 0) return; // ✅ lekin n manfiy bo'lsa?
console.log(n);
sanash(n - 1);
}
function sanash(n) {
if (n <= 0) return; // ✅ manfiy sonlar uchun ham xavfsiz
console.log(n);
sanash(n - 1);
}
Katta n bilan stack overflow:
faktorial(100000); // ❌ RangeError — call stack to'lib ketadi
Juda katta n uchun — sikl yoki tail recursion (JavaScript da to'liq qo'llab-quvvatlanmaydi).
Fibonacci ni memoizationsiz ishlatish:
fibonacci(40); // ❌ juda sekin — milliardlab chaqiruv
fibonacci(40, {}); // ✅ memoization bilan tez
Xulosa
Recursion — funksiyaning o'z-o'zini chaqirishi. Har bir rekursiv funksiyada base case (to'xtash) va recursive case (kichikroq muammo) bo'lishi shart.
Recursion — daraxt tuzilishi, ichma-ich ma'lumotlar uchun tabiiy. Oddiy takrorlash uchun sikl yaxshiroq.
Fibonacci kabi takrorlanadigan hisoblashlarda — memoization ishlatiladi.
Keyingi dars
16-dars: IIFE — Immediately Invoked Function Expression