Funksiyalarning o'z-o'zini chaqirishi (rekursiya), Base Case va Recursive Case ahamiyati, Stack Overflow xatosi, rekursiya va sikllar solishtirmasi hamda ichma-ich (nested) obyektlar va massivlarni qayta ishlash.
Recursion (rekursiya) — function o'zini-o'zi chaqirishi. Bu bobda rekursiyaning ishlash mexanizmini, uni qachon ishlatish kerakligini va uning xavflarini chuqur o'rganamiz.
Recursion — muammoni, o'sha muammoning kichikroq nusxasi orqali hal qilish, va function ichida o'zining o'zini qayta chaqirish.
🪆 Recursion — bu matryoshka (rus qo'g'irchog'i): har bir katta qo'g'irchoq ichida — xuddi shu qo'g'irchoqning kichikroq nusxasi bor, toki eng kichik, ichi bo'sh qo'g'irchoqqa (asosiy holat) yetguncha.
script.jsJavaScriptfunction factorial(n) {if (n <= 1) {return 1; // Base case (asosiy holat)}return n * factorial(n - 1); // Recursive case (rekursiv holat)}console.log(factorial(5)); // 120 (5 × 4 × 3 × 2 × 1)
code.textTEXTfactorial(5)= 5 * factorial(4)= 5 * (4 * factorial(3))= 5 * (4 * (3 * factorial(2)))= 5 * (4 * (3 * (2 * factorial(1))))= 5 * (4 * (3 * (2 * 1)))= 120
Har bir rekursiv function — majburiy ravishda "to'xtash shartiga" (base case) ega bo'lishi kerak, aks holda u abadiy o'zini chaqiraveradi.
script.jsJavaScriptfunction badFactorial(n) {return n * badFactorial(n - 1); // base case YO'Q!}badFactorial(5); // ❌ "Maximum call stack size exceeded"
Nima uchun bunday bo'ladi? Bu — Chapter 29'da o'rgangan Call Stackning to'lib ketishi (Stack Overflow). Har bir rekursiv chaqiruv — Call Stack'ga yangi "qatlam" qo'shadi (Chapter 29.2). Base case bo'lmagani uchun, function hech qachon returnga yetib bormaydi, va chaqiruvlar cheksiz to'planaveradi, toki brauzer/Node.js xotira chegarasiga yetguncha.
code.textTEXTbadFactorial(5)badFactorial(4)badFactorial(3)badFactorial(2)badFactorial(1)badFactorial(0)badFactorial(-1)badFactorial(-2)... (abadiy davom etadi, Stack TO'LIB KETADI)
🔥 Important
Har bir rekursiv function yozishda, birinchi navbatda base caseni aniq belgilash kerak — "qachon rekursiya to'xtashi kerak?" degan savolga javob bermasdan, rekursiv qismni yozmang.
| Xususiyat | Loop (Chapter 5) | Recursion |
|---|---|---|
| O'qish qulayligi | Ba'zida murakkab mantiq uchun uzun | Ko'p hollarda qisqa va tabiiy (masalan, daraxt tuzilmalar) |
| Xotira | Kam (bitta o'zgaruvchi holat) | Ko'p (har chaqiruv Call Stack'da joy oladi) |
| Tezlik | Odatda tezroq | Odatda sekinroq (function chaqiruv "xarajati") |
| Mos vazifalar | Oddiy, tekis takrorlash | Daraxt, ichma-ich tuzilmalar, "bo'l va hukmronlik qil" |
script.jsJavaScriptfunction factorialLoop(n) {let result = 1;for (let i = 2; i <= n; i++) {result *= i;}return result;}console.log(factorialLoop(5)); // 120
Oddiy hollarda (masalan, faktorial), loop odatda rekursiyadan tezroq va xotira jihatidan tejamliroq. Lekin ba'zi muammolar (masalan, ichma-ich daraxt tuzilmalarni aylanib chiqish) — rekursiya bilan ancha tabiiy va sodda ifodalanadi.
Recursion'ning haqiqiy kuchi — chuqurligi oldindan noma'lum bo'lgan ma'lumot tuzilmalari (masalan, ichma-ich object'lar, papka tuzilmalari, DOM daraxti) bilan ishlashda namoyon bo'ladi, bu yerda oddiy loop bilan yechim topish ancha qiyin.
script.jsJavaScriptlet nestedObject = {name: "Ali",address: {city: "Toshkent",details: {zip: "100000",country: {name: "O'zbekiston"}}}};function flattenObject(obj, result = {}) {for (let key in obj) {if (typeof obj[key] === "object" && obj[key] !== null) {flattenObject(obj[key], result); // REKURSIV chaqiruv} else {result[key] = obj[key];}}return result;}console.log(flattenObject(nestedObject));// { name: "Ali", city: "Toshkent", zip: "100000", name: "O'zbekiston" }
flattenObject — har bir key uchun, agar qiymat yana object bo'lsa, o'zini-o'zi shu ichki object bilan qayta chaqiradi.💡 Key Idea
Bu misolda, obj[key] object bo'lmasligi — base case vazifasini o'taydi (Chapter 46.2'dagi qoida bilan bir xil tamoyil, faqat bu safar sonlar emas, tuzilma chuqurligi asosida).
code.textTEXTflattenObject(nestedObject)│├── name: "Ali" → natijaga qo'shiladi│└── address (object!) → REKURSIYA│├── city: "Toshkent" → natijaga qo'shiladi│└── details (object!) → REKURSIYA│├── zip: "100000" → natijaga qo'shiladi│└── country (object!) → REKURSIYA│└── name: "O'zbekiston" → natijaga qo'shiladi
🔗 Connection
Bu — Chapter 32'da ko'rgan DOM daraxtini aylanib chiqishning ham asosiy tamoyili: DOM elementlari ham "ichma-ich" tuzilma bo'lgani uchun, ular ustida ishlashda rekursiya juda tabiiy yechim hisoblanadi.
script.jsJavaScriptfunction sumArray(arr) {if (arr.length === 0) {return 0; // Base case}return arr[0] + sumArray(arr.slice(1)); // Recursive case}console.log(sumArray([1, 2, 3, 4, 5])); // 15
Har bir chaqiruvda, array'ning birinchi elementi ajratib olinadi va qolgan qismi (slice(1) — Chapter 7) bilan rekursiv chaqiriladi, toki array bo'sh (length === 0) bo'lguncha.
⚠️ Common Mistake
Katta array'lar uchun bu usul (har safar slice() bilan yangi array yaratish) — samarasiz bo'lishi mumkin, chunki har bir chaqiruv yangi array nusxasini yaratadi. Katta ma'lumotlar uchun, odatda oddiy loop yoki reduce() (Chapter 14) afzalroq.
Task:
countdown(n) nomli rekursiv function yozing — u ndan 1gacha sonlarni konsolga chiqarsin, keyin "Tayyor!" deb yozsin. Base case'ni to'g'ri belgilang.
Required Concepts:
if (n === 0) { console.log("Tayyor!"); return; }Task:
sumArray(arr) functionini Chapter 46.5'dagi kabi emas, balki index parametri bilan (har safar yangi array yaratmasdan) qayta yozing.
Required Concepts:
function sumArray(arr, index = 0) { if (index === arr.length) return 0; return arr[index] + sumArray(arr, index + 1); }Task:
Ichma-ich array'ni (Chapter 7.6'dagi kabi, lekin bu safar chuqurligi noma'lum) to'liq "tekislaydigan" (flatten) rekursiv function yozing, flat(Infinity)dan foydalanmasdan:
script.jsJavaScriptlet nested = [1, [2, [3, [4, [5]]]]];// Natija: [1, 2, 3, 4, 5]
Required Concepts:
Array.isArray()flatten() chaqiring va natijani concat qiling.script.jsJavaScriptfunction mystery(n) {if (n <= 0) {return 0;}return n + mystery(n - 1);}console.log(mystery(4));
🧠 Think first! Javobni ko'rishdan oldin kamida 30 soniya o'ylang.
<details> <summary>Answer</summary> 10Why?
code.textTEXTmystery(4) = 4 + mystery(3)= 4 + (3 + mystery(2))= 4 + (3 + (2 + mystery(1)))= 4 + (3 + (2 + (1 + mystery(0))))= 4 + (3 + (2 + (1 + 0)))= 10
Bu function — 1dan ngacha bo'lgan sonlarning yig'indisini rekursiv tarzda hisoblaydi.
</details>Quyidagi kodda muammo bor:
script.jsJavaScriptfunction sumUpTo(n) {return n + sumUpTo(n - 1);}console.log(sumUpTo(5));
Your Task:
Yechim:
</details>script.jsJavaScriptfunction sumUpTo(n) {if (n <= 0) {return 0; // BASE CASE}return n + sumUpTo(n - 1);}console.log(sumUpTo(5)); // 15
script.jsJavaScriptfunction recursiveFunc(n) {if (/* base case shart */) {return /* asosiy natija */;}return /* n bilan bog'liq amal */ + recursiveFunc(/* kichikroq qiymat */);}// Nested tuzilma uchunfunction traverse(obj) {for (let key in obj) {if (typeof obj[key] === "object") {traverse(obj[key]); // rekursiya} else {// ishlov berish}}}
Mavzu bo‘yicha tushunchalaringizni interaktiv test orqali sinovdan o‘tkazing va natijalarni bilib oling.
Darslikda o‘rganilgan qoidalarga asosan real kod yozing, testlardan o‘tkazing va yechimlarni mustahkamlang.
Test Sinovi
Hali topshirilmagan (kamida 75% kerak)
Amaliy Masalalar
0 / 3 ta yechildi (kamida 75% kerak)
Dars sifati va mazmunini yaxshilash uchun o‘z haqqoniy bahoingizni qoldiring.
Mavzuni to‘liq o‘zlashtirish uchun test sinovi, amaliy kodlash va bog‘liq darslar
O‘z bilimingizni interaktiv test savollari orqali tekshiring va darhol xatolarni tahlil qiling.
Nazariyani real kod yozish bilan mustahkamlang, avtomatik testlardan o‘ting va XP to‘plang.
Kurs bo‘yicha keyingi va bog‘liq mavzular: