IlmHamroh
JavaScript Full-stack/8-qism. JavaScript asoslari45/61-dars19 daqiqa
Mundarija (29)

JavaScript rekursiya: funksiya o'zini o'zi chaqirganda

Qisqacha: Rekursiya — funksiyaning o'z ichida o'zini chaqirishi. Har rekursiv funksiyada ikki qism bo'ladi: to'xtash sharti (base case) va masalani bir qadam kichraytirib o'zini qayta chaqirish. To'xtash sharti bo'lmasa, dastur RangeError: Maximum call stack size exceeded xatosi bilan to'xtaydi.

Bu darsda

  • Rekursiya nima ekanini va u qaysi masalalarda qulay ekanini tushunasiz.
  • Base case va rekursiv qadamdan iborat funksiya yoza olasiz.
  • Chaqiruvlar steki (call stack) qanday o'sib, qanday bo'shashini qog'ozda chiza olasiz.
  • RangeError: Maximum call stack size exceeded xatosining sababini topa olasiz.
  • Rekursiya va sikl o'rtasida to'g'ri tanlov qila olasiz.

Oldin bilishingiz kerak: Funksiya e'lon qilish va chaqirish, return va erta qaytish, Pure funksiya va side effect.

1. Nega bu kerak?

O'tgan darsda pure funksiyalar bilan tanishdik: bir xil kirishga doim bir xil javob. Bugun funksiyaning yana bir g'aroyib qobiliyatini ko'ramiz — u o'zini o'zi chaqira oladi.

Tasavvur qiling: ertalab non do'koni oldida uzun navbat. Jasur oxirida turibdi va oldida nechta odam borligini bilmoqchi. Lekin navbat boshini ko'ra olmaydi.

Jasur oldidagi odamdan so'raydi: "Sizdan oldin nechta odam bor?" U ham bilmaydi va o'z oldidagidan so'raydi. Savol shu tarzda oldinga uzatiladi. Eng oldindagi odam: "Mendan oldin hech kim yo'q — 0" deydi.

Endi javob orqaga qaytadi. Har bir odam eshitgan soniga 1 qo'shib, orqasidagiga aytadi. Oxiri Jasur aniq javobni oladi. Hech kim butun navbatni sanamadi.

Bu usulning nomi — rekursiya. Unda ikki narsa muhim:

  • savol har safar kichikroq masalaga uzatiladi (navbat bir odamga qisqaradi);
  • qayerdadir to'xtash joyi bor (eng oldindagi odam aniq javob biladi).

2. Rekursiya nima?

Rekursiya (recursion) — funksiyaning o'z tanasi ichida o'zini chaqirishi. Bunday funksiya rekursiv funksiya deyiladi.

Eng sodda misol — raketa uchirishdan oldingi teskari sanoq:

js
function sanoq(n) {
  if (n === 0) {
    console.log("Start!");
    return;
  }
  console.log(n);
  sanoq(n - 1);
}

sanoq(3);

Konsolda:

text
3
2
1
Start!

Keling, sekin ko'ramiz. sanoq(3) chaqirildi. n — 3, u 0 emas, shuning uchun 3 chiqadi. Keyin funksiya o'zini sanoq(2) deb chaqiradi. U 2 ni chiqaradi va sanoq(1) ni chaqiradi. sanoq(0) ga yetganda shart bajariladi: Start! chiqadi va return funksiyani to'xtatadi.

Diqqat: return faqat sanoq(0) ni to'xtatadi, hammasini emas. Undan keyin sanoq(1), sanoq(2), sanoq(3) navbat bilan o'z ishini tugatadi. Ularda sanoq(n - 1) dan keyin boshqa qator yo'q, shuning uchun konsolda hech narsa qo'shilmaydi. Bu "orqaga qaytish"ni 4-bo'limda batafsil ko'ramiz.

2.1 Ikki majburiy qism

Har bir rekursiv funksiya ikki qismdan iborat:

Qism Nima qiladi sanoq da Navbatda
To'xtash sharti (base case) Javobni o'zi biladi, qayta chaqirmaydi if (n === 0) Eng oldindagi odam: "0"
Rekursiv qadam Masalani kichraytirib, o'zini chaqiradi sanoq(n - 1) "Oldingizdan so'rab bering"

To'xtash sharti (base case) — rekursiya to'xtaydigan eng kichik holat. Unda funksiya o'zini boshqa chaqirmaydi. Dasturchilar ko'pincha inglizcha "base case" deydi, shuning uchun bu darsda ikkalasi ham uchraydi — ma'nosi bir xil.

Rekursiv qadam — funksiya o'zini kichikroq qiymat bilan chaqiradigan qism. "Kichikroq" degani — base case'ga bir qadam yaqinroq.

Diqqat: Rekursiv funksiya yozayotganda birinchi savol doim bitta: "Bu funksiya qachon to'xtaydi?" Javob topilmasa — funksiya hali tayyor emas.

2.2 Xuddi shu ishni sikl ham qiladi

sanoq ni for sikli bilan ham yozsa bo'ladi:

js
function sanoqSikl(n) {
  for (let i = n; i > 0; i--) {
    console.log(i);
  }
  console.log("Start!");
}

sanoqSikl(3);

Konsolda:

text
3
2
1
Start!

Natija bir xil. Haqiqatan ham, har bir rekursiyani sikl bilan yozish mumkin (va aksincha). Unda rekursiya nimaga kerak? Ba'zi masalalarda rekursiv yechim ancha qisqa va tushunarli chiqadi. Buni dars davomida ko'rasiz.

Tekshirib ko'ring: Quyidagi funksiyada base case qaysi qator, rekursiv qadam qaysi?

js
function salomlar(n) {
  if (n <= 0) {
    return;
  }
  console.log("Salom!");
  salomlar(n - 1);
}
Javob

Base case — if (n <= 0) { return; } qismi. Bu yerda funksiya o'zini chaqirmay to'xtaydi. Rekursiv qadam — salomlar(n - 1);. U masalani bittaga kichraytiradi. salomlar(3) uch marta "Salom!" chiqaradi: 3, 2, 1 uchun, 0 da esa to'xtaydi.

3. Qiymat qaytaradigan rekursiya

sanoq faqat konsolga yozdi. Ko'pincha rekursiv funksiya qiymat qaytaradi — xuddi navbatdagi odamlar javobni orqaga uzatgandek.

3.1 1 dan n gacha yig'indi

Malika do'konida har kuni savdo bittaga ko'payadi deylik: 1-kuni 1 ta, 2-kuni 2 ta va shunday davom etadi. 4 kunda jami nechta? 1 + 2 + 3 + 4 = 10.

Buni rekursiv o'ylaymiz: 4 gacha yig'indi — bu 4 + (3 gacha yig'indi). 3 gacha yig'indi — 3 + (2 gacha yig'indi). Eng kichik holat: 0 gacha yig'indi — 0.

js
function yigindi(n) {
  if (n === 0) {
    return 0; // base case: javob tayyor
  }
  return n + yigindi(n - 1); // rekursiv qadam
}

console.log(yigindi(4)); // 10
console.log(yigindi(100)); // 5050

Diqqat qiling: return n + yigindi(n - 1). Funksiya avval kichikroq masalaning javobini kutadi, keyin unga n ni qo'shib qaytaradi.

3.2 Faktorial

Faktorial — 1 dan n gacha bo'lgan sonlar ko'paytmasi. Matematikada u undov belgisi bilan yoziladi: 5! = 5 × 4 × 3 × 2 × 1 = 120. Masalan, 5 ta mehmonni stolga necha xil tartibda o'tqazish mumkin? Aynan 120 xil.

Bu yerdagi ! — matematik belgi. Mantiqiy operatorlar darsidagi ! ("emas") bilan aloqasi yo'q. JavaScript'da 5! deb yozib bo'lmaydi — faktorialni o'zimiz funksiya qilib yozamiz.

Rekursiv ta'rifi yig'indiga juda o'xshaydi: 5! = 5 × 4!. Eng kichik holat: 1! = 1.

js
function faktorial(n) {
  if (n <= 1) {
    return 1; // base case
  }
  return n * faktorial(n - 1); // rekursiv qadam
}

console.log(faktorial(5)); // 120
console.log(faktorial(0)); // 1

Base case'da n === 1 emas, n <= 1 yozdik. Shunda faktorial(0) ham to'g'ri ishlaydi — matematikada 0! = 1 (matematiklar shunday kelishgan).

3.3 Rekursiv funksiya yozishning 3 savoli

Har rekursiv masalada o'zingizga uchta savol bering:

  1. Eng kichik holat qaysi va uning javobi nima? (n <= 1 → 1)
  2. Masalani qanday qilib bir qadam kichraytiraman? (n → n - 1)
  3. Kichikroq javobdan kattasini qanday yasayman? (n * faktorial(n - 1))

Uchinchi savolda bir sir bor. faktorial(n - 1) ichida nima bo'lishini o'ylab o'tirmang. Unga ishoning: u to'g'ri javob qaytaradi. Siz faqat bitta qadamni to'g'ri yozing.

Tekshirib ko'ring: yigindi(3) ichida yigindi funksiyasi jami necha marta chaqiriladi?

Javob

4 marta: yigindi(3), yigindi(2), yigindi(1) va yigindi(0). Oxirgisi base case — u o'zini boshqa chaqirmaydi. Umumiy qoida: yigindi(n) jami n + 1 marta chaqiriladi.

4. Chaqiruvlar steki (call stack)

Funksiya chaqirish darsida bir qoidani ko'rgan edik: funksiya ishini tugatib, chaqirilgan joyga qaytadi. Rekursiyada bu qoida juda muhim. Axir funksiya o'zini chaqirganda, u hali tugamagan bo'ladi-ku!

4.1 JavaScript "qayerga qaytish"ni qanday eslaydi?

Chaqiruvlar steki (call stack) — JavaScript hozir bajarilayotgan funksiyalarni saqlaydigan ro'yxat. Har bir chaqiruv ro'yxat tepasiga qo'shiladi. Funksiya tugaganda — tepadan olib tashlanadi.

Hayotdan o'xshatish: to'ydan keyin yuvilgan likoplar taxlami. Yangi likopni doim tepaga qo'yasiz. Olganda ham tepadan olasiz. Eng oxirgi qo'yilgani birinchi olinadi.

faktorial(3) chaqirilganda stek shunday o'zgaradi:

Qadam Stek (pastdan tepaga) Nima bo'lyapti
1 faktorial(3) 3 * faktorial(2) — javobni kutyapti
2 faktorial(3), faktorial(2) 2 * faktorial(1) — kutyapti
3 faktorial(3), faktorial(2), faktorial(1) base case: 1 qaytaradi
4 faktorial(3), faktorial(2) 2 * 1 = 2 qaytaradi
5 faktorial(3) 3 * 2 = 6 qaytaradi
6 (bo'sh) natija: 6

Ko'ryapsizmi? Avval stek o'sadi (1–3-qadamlar) — har yangi chaqiruv likopdek tepaga qo'yiladi. Keyin stek bo'shaydi (4–6-qadamlar) — tepadagi chaqiruv javobini beradi va olib tashlanadi. Uning javobi ostidagi chaqiruvga uzatiladi. Navbatdagi savol va javobga o'xshaydi: savol oldinga ketadi, javob orqaga qaytadi.

Endi shu jarayonni qadamma-qadam o'zingiz yurib ko'ring. Keyingi tugmasini bosing va stek qanday o'sib, keyin bo'shashini kuzating:

4.2 Stekni o'z ko'zingiz bilan ko'ring

Keling, funksiyaga console.log qo'shib, har bir chaqiruvni kuzatamiz. O'tgan darsda "hisoblovchi funksiya return qilsin" degan edik — bu qoida o'z kuchida. Bu yerdagi console.log lar faqat kuzatish uchun: ishni tushunib olgach, ularni olib tashlaymiz.

chuqurlik parametri — nechta chaqiruv ichida turganimiz. U faqat chekinish (qator boshidagi bo'sh joy) uchun. " ".repeat(chuqurlik) — to'rtta bo'sh joyni chuqurlik marta takrorlaydi (repeat metodi):

js
function faktorial(n, chuqurlik = 0) {
  const chekinish = "    ".repeat(chuqurlik);
  console.log(`${chekinish}faktorial(${n}) boshlandi`);
  if (n <= 1) {
    console.log(`${chekinish}faktorial(${n}) → 1`);
    return 1;
  }
  const natija = n * faktorial(n - 1, chuqurlik + 1);
  console.log(`${chekinish}faktorial(${n}) → ${natija}`);
  return natija;
}

faktorial(3);

Konsolda:

text
faktorial(3) boshlandi
    faktorial(2) boshlandi
        faktorial(1) boshlandi
        faktorial(1) → 1
    faktorial(2) → 2
faktorial(3) → 6

Chekinish chuqurlashgan joy — stek o'sayotgan payt. Chekinish qaytgan joy — stek bo'shayotgan payt. faktorial(3) boshlandi birinchi chiqdi, lekin uning javobi eng oxirida chiqdi. Chunki u boshqalarning javobini kutib turdi.

Maslahat: console API darsida ko'rgan console.trace() ham stekni ko'rsatadi. Uni base case ichiga qo'ysangiz, konsolda at faktorial qatorlari ketma-ket chiqadi — har biri stekdagi bitta chaqiruv. Stekni qadamma-qadam kuzatishning eng qulay yo'li esa — brauzer DevTools'i. Uni ikki darsdan keyin o'rganamiz.

4.3 Stek cheksiz emas

Likop taxlami shiftgacha yetsa, qulaydi. Chaqiruvlar steki ham shunday: uning hajmi cheklangan. Node.js'da oddiy funksiya uchun bu chegara taxminan 10 000 chaqiruv atrofida. Aniq son muhitga va funksiyaga qarab farq qiladi.

Chegaradan oshsa, JavaScript dasturni xato bilan to'xtatadi:

text
RangeError: Maximum call stack size exceeded

Tarjimasi: "Chaqiruvlar stekining eng katta hajmidan oshib ketildi". Inglizchada bu holat stack overflow ("stek to'lib ketishi") deyiladi. Dasturchilarning mashhur savol-javob sayti Stack Overflow ham aynan shu xato nomini olgan.

Tekshirib ko'ring: faktorial(4) chaqirilganda stekda bir vaqtning o'zida eng ko'pi bilan nechta faktorial chaqiruvi bo'ladi?

Javob

4 ta: faktorial(4), faktorial(3), faktorial(2), faktorial(1). faktorial(1) base case'ga yetgan paytda qolgan uchtasi hali javob kutib turibdi. Shuning uchun hammasi bir vaqtda stekda bo'ladi.

5. Yana bir necha rekursiv misol

5.1 Darajaga ko'tarish

2¹⁰ — o'nta ikkini o'zaro ko'paytirish: 2 × 2 × ... × 2. Amalda buni ** operatori bilan yozasiz: 2 ** 10. Bu yerda esa rekursiyani mashq qilish uchun o'zimiz yozamiz. Rekursiv o'ylaymiz: 2¹⁰ = 2 × 2⁹. Eng kichik holat: har qanday sonning 0-darajasi 1 ga teng.

js
function daraja(asos, korsatkich) {
  if (korsatkich === 0) {
    return 1; // base case: x⁰ = 1
  }
  return asos * daraja(asos, korsatkich - 1);
}

console.log(daraja(2, 10)); // 1024
console.log(daraja(5, 3)); // 125

Bu yerda ikki parametr bor, lekin kichrayadigani faqat bittasi — korsatkich. asos har chaqiruvda o'zgarmay uzatiladi.

5.2 Sonning raqamlari yig'indisi

Endi o'zingiz qatnashasiz. Vazifa: 1234 sonining raqamlari yig'indisini topish: 1 + 2 + 3 + 4 = 10.

Ikki yordamchi amalni eslaymiz:

  • 1234 % 10 — oxirgi raqam, ya'ni 4.
  • Math.floor(1234 / 10) — oxirgi raqamsiz son, ya'ni 123.

Demak: 1234 raqamlari yig'indisi = 4 + (123 raqamlari yig'indisi).

Eng kichik holat qaysi? Son bitta raqamdan iborat bo'lsa (10 dan kichik), uning yig'indisi — sonning o'zi. Masalan, raqamlarYigindisi(7) qancha qaytaradi?

raqamlarYigindisi(56) esa 6 + raqamlarYigindisi(5) ga teng. Natija nechchi?

Endi to'liq kod:

js
function raqamlarYigindisi(son) {
  if (son < 10) {
    return son; // base case: bitta raqam
  }
  const oxirgi = son % 10;
  const qolgani = Math.floor(son / 10);
  return oxirgi + raqamlarYigindisi(qolgani);
}

console.log(raqamlarYigindisi(1234)); // 10
console.log(raqamlarYigindisi(2026)); // 10
console.log(raqamlarYigindisi(7)); // 7

5.3 Fibonachchi: bir funksiya — ikki chaqiruv

Fibonachchi sonlari — har bir son oldingi ikkitasining yig'indisi bo'lgan qator: 0, 1, 1, 2, 3, 5, 8, 13, 21, ....

Rekursiv ta'rif: fib(n) = fib(n - 1) + fib(n - 2). Eng kichik holatlar: fib(0) = 0, fib(1) = 1.

js
function fib(n) {
  if (n <= 1) {
    return n; // fib(0) = 0, fib(1) = 1
  }
  return fib(n - 1) + fib(n - 2); // ikkita chaqiruv!
}

console.log(fib(7)); // 13
console.log(fib(10)); // 55

Kod chiroyli, lekin uning jiddiy kamchiligi bor. Har chaqiruv ikkita yangi chaqiruv tug'diradi. fib(5) qanday tarqalishini qavatma-qavat ko'ring. Har qavatdagi chaqiruvlarni oldingi qavatdagilar chaqirgan. fib(1) va fib(0) — to'xtash sharti, ular hech kimni chaqirmaydi:

Qavat Chaqiruvlar Soni
1 fib(5) 1
2 fib(4), fib(3) 2
3 fib(3), fib(2), fib(2), fib(1) 4
4 fib(2), fib(1), fib(1), fib(0), fib(1), fib(0) 6
5 fib(1), fib(0) 2

Jami: 1 + 2 + 4 + 6 + 2 = 15 ta chaqiruv. fib(3) ikki marta, fib(2) uch marta qayta hisoblanyapti. Bir xil ish qayta-qayta bajarilyapti. n kattalashgan sari chaqiruvlar soni keskin oshadi:

n fib(n) Chaqiruvlar soni
5 5 15
10 55 177
20 6 765 21 891
30 832 040 2 692 537
40 102 334 155 331 160 281

fib(40) uchun 331 milliondan ortiq chaqiruv! Kompyuter bir-ikki soniya "o'ylanib" qoladi. fib(50) esa daqiqalab hisoblanadi.

Maslahat: Bu muammoning chiroyli yechimi bor: bir marta hisoblangan javobni eslab qolish. Bu usul memoization deyiladi, saqlangan javoblar esa kesh deb ataladi. Hozir bilish shart emas — uni memoization darsida o'rganamiz. Hozircha qoida: bitta funksiya o'zini ikki marta chaqirsa — tezlikka e'tibor bering.

5.4 Ichma-ich tuzilmalar — rekursiyaning uyi

Rekursiya eng tabiiy ishlaydigan joy — ichma-ich tuzilmalar. Matryoshka qo'g'irchog'ini eslang: ochsangiz — ichida yana matryoshka, uni ochsangiz — yana biri.

Hozircha rekursiyani oddiy matn bilan mashq qilamiz. Massiv va obyektlarni o'rgangach, haqiqiy papkalar daraxtini aynan shu usulda aylanib chiqamiz.

Sovg'a qutilar ichiga joylangan deylik. Har bir [ va ] jufti — bitta quti: "[[[soat]]]". Nechta quti ochish kerak?

js
function qutilarSoni(sovga) {
  if (!sovga.startsWith("[")) {
    return 0; // base case: quti qolmadi, sovg'aning o'zi
  }
  const ichi = sovga.slice(1, -1); // tashqi qutini ochamiz
  return 1 + qutilarSoni(ichi);
}

console.log(qutilarSoni("[[[soat]]]")); // 3
console.log(qutilarSoni("[telefon]")); // 1
console.log(qutilarSoni("kitob")); // 0

Real dasturlarda bunday tuzilmalar ko'p. Kompyuteringizdagi papkalar ichida papkalar bor. HTML'da <div> ichida <div>. Telegram izohiga javob, javobga yana javob yoziladi. Bunday ma'lumot daraxtsimon deyiladi. Uni aylanib chiqishning eng qulay yo'li — rekursiya. Massivlar va obyektlarni o'rganganimizdan keyin bunday daraxtlar bilan ham ishlaymiz.

Tekshirib ko'ring: fib(4) chaqirilganda fib(2) necha marta hisoblanadi?

Javob

2 marta. fib(4) → fib(3) va fib(2). fib(3) esa yana fib(2) va fib(1) ni chaqiradi. Shunday qilib fib(2) bir marta to'g'ridan-to'g'ri, bir marta fib(3) ichida hisoblanadi.

6. Rekursiya yoki sikl?

Ikkalasi ham takrorlash vositasi. Qaysi birini tanlash kerak?

Holat Tanlov Nega
Oddiy takror (sanoq, yig'indi) Sikl Stek to'lmaydi, tezroq
Juda ko'p qadam (10 000+) Sikl Rekursiyada RangeError chiqadi
Ichma-ich tuzilma (papka, HTML, izohlar) Rekursiya Kod qisqa va tabiiy
Masala o'zi rekursiv ta'riflangan Rekursiya Ta'rif kodga to'g'ridan-to'g'ri o'tadi

Masalan, rekursiv yigindi(100000) stekni to'ldirib yuboradi. Sikl bilan esa muammosiz ishlaydi:

js
function yigindiSikl(n) {
  let jami = 0;
  for (let i = 1; i <= n; i++) {
    jami += i;
  }
  return jami;
}

console.log(yigindiSikl(100000)); // 5000050000

7. Ko'p uchraydigan xatolar

7.1 Base case'ni unutish

js
function sanoq(n) {
  console.log(n);
  sanoq(n - 1);
}

sanoq(3);

Konsolda avval minglab son chiqadi: 3, 2, 1, 0, -1, -2.... Keyin qizil xato:

text
RangeError: Maximum call stack size exceeded

Funksiyada to'xtash sharti yo'q. U o'zini cheksiz chaqiradi va stek to'lib ketadi. Tuzatish: base case qo'shing — if (n <= 0) { return; }. Nega === emas, <= ekanini keyingi xatoda ko'ramiz.

7.2 Base case bor, lekin unga yetib bo'lmaydi

Bu xato ayyorroq. Juft sonlarni sanaymiz, har qadamda 2 ga kamaytiramiz:

js
function juftSanoq(n) {
  if (n === 0) {
    return;
  }
  console.log(n);
  juftSanoq(n - 2);
}

juftSanoq(5);

Natija yana RangeError: Maximum call stack size exceeded. Nega? 5 → 3 → 1 → -1 → -3... — n hech qachon aynan 0 bo'lmaydi. Base case'ni "sakrab o'tib" ketdik.

Tuzatish: qat'iy tenglik o'rniga chegarani tekshiring. Oldingi sanoq, yigindi misollarida === 0 yetarli edi: n birma-bir kamayadi va 0 ni sakrab o'tmaydi (musbat son berilsa). Lekin qadam katta bo'lsa yoki manfiy son kelsa — <= kerak. Shuning uchun base case'da <= ko'pincha xavfsizroq:

js
function juftSanoq(n) {
  if (n <= 0) {
    return;
  }
  console.log(n);
  juftSanoq(n - 2);
}

juftSanoq(5);

Konsolda:

text
5
3
1

7.3 Rekursiv chaqiruvda return ni unutish

js
function yigindi(n) {
  if (n === 0) {
    return 0;
  }
  n + yigindi(n - 1); // return yo'q!
}

console.log(yigindi(3)); // undefined

Xato chiqmaydi, lekin javob — undefined. Hisob bajarildi, ammo natija hech qayerga qaytarilmadi. return darsida ko'rganimizdek, returnsiz funksiya undefined beradi. Tuzatish: return n + yigindi(n - 1);.

8. Mashqlar

1-mashq (oson): Takrorlovchi

takrorlabYoz(matn, n) rekursiv funksiyasini yozing. U matn ni konsolga n marta chiqarsin. for sikli ishlatmang. Tekshirish: takrorlabYoz("Olg'a, IlmHamroh!", 3).

Yechim
js
function takrorlabYoz(matn, n) {
  if (n <= 0) {
    return; // base case: boshqa chiqarish kerak emas
  }
  console.log(matn);
  takrorlabYoz(matn, n - 1);
}

takrorlabYoz("Olg'a, IlmHamroh!", 3);

Konsolda:

text
Olg'a, IlmHamroh!
Olg'a, IlmHamroh!
Olg'a, IlmHamroh!

matn o'zgarmaydi, kichrayadigani faqat n. Base case n <= 0 — manfiy son berilsa ham funksiya to'xtaydi.

2-mashq (o'rta): So'zni teskari yozish

teskari(soz) rekursiv funksiyasini yozing. U so'zni teskari tartibda qaytarsin: "salom" → "molas".

Maslahat: so'zning teskarisi = (birinchi harfsiz qismining teskarisi) + birinchi harf. soz.slice(1) — birinchi harfsiz qism, soz[0] — birinchi harf. Eng kichik holat: so'z bo'sh yoki bitta harfli bo'lsa, uning teskarisi — o'zi.

Yechim
js
function teskari(soz) {
  if (soz.length <= 1) {
    return soz; // bo'sh yoki bitta harf — teskarisi o'zi
  }
  return teskari(soz.slice(1)) + soz[0];
}

console.log(teskari("salom")); // molas
console.log(teskari("Toshkent")); // tnekhsoT
console.log(teskari("a")); // a

teskari("uch") qanday ishlaydi: teskari("ch") + "u" → (teskari("h") + "c") + "u" → "h" + "c" + "u" → "hcu". Base case'da length <= 1 yozdik — bo'sh satr "" berilsa ham to'xtaydi.

3-mashq (qiyin): Palindrom

Palindrom — oldindan ham, orqadan ham bir xil o'qiladigan so'z: "kiyik", "non", "tut". palindrommi(soz) rekursiv funksiyasini yozing. U true yoki false qaytarsin.

Maslahat: birinchi va oxirgi harfni solishtiring. Teng bo'lmasa — false. Teng bo'lsa — o'rtadagi qismni (soz.slice(1, -1)) tekshiring. Birinchi harf — soz[0], oxirgi harf — soz.at(-1) (satr asoslari darsida ko'rgan edik; soz[-1] esa undefined beradi). Eng kichik holatni o'zingiz toping.

Yechim
js
function palindrommi(soz) {
  if (soz.length <= 1) {
    return true; // bo'sh yoki bitta harf — doim palindrom
  }
  if (soz[0] !== soz.at(-1)) {
    return false; // chetlari farq qildi — yetarli
  }
  return palindrommi(soz.slice(1, -1));
}

console.log(palindrommi("kiyik")); // true
console.log(palindrommi("non")); // true
console.log(palindrommi("olma")); // false

Bu funksiyada ikkita to'xtash joyi bor. Birinchisi — so'z tugab qolsa (true). Ikkinchisi — chetlari farq qilsa (false, erta qaytish). "kiyik" → "iyi" → "y" → true. Katta-kichik harfni ham hisobga olish uchun avval soz.toLowerCase() qilish mumkin.

9. Real ishda

  • Fayllar tizimi: VS Code chap panelidagi papkalar daraxti rekursiv chiziladi. Node.js'da papka ichidagi hamma fayllarni topish ham rekursiya bilan yoziladi.
  • HTML va DOM: brauzer sahifani elementlar daraxti sifatida saqlaydi (bu daraxt DOM deyiladi — keyingi qismda o'rganamiz). Uni aylanib chiqish — klassik rekursiya.
  • Izohlar va menyular: Telegram'dagi javobga javob, do'kon saytidagi "Kategoriya → Bo'lim → Tovar" menyusi.
  • Algoritmlar: ro'yxatni tez tartiblash usullari (merge sort, quick sort) va qidiruv algoritmlari rekursiyaga tayanadi. Ularni algoritmlar qismida o'rganamiz — hozir nomini bilish shart emas.
  • Intervyu: faktorial, Fibonachchi, satrni teskari aylantirish — boshlang'ich dasturchi intervyusining klassik savollari. "Rekursiyaning xavfi nima?" deb so'rashsa: stack overflow va bir xil ishni qayta hisoblash.

Rekursiv fikrlashni chuqurroq algoritmlar modulida davom ettiramiz.

Xulosa

  • Rekursiya — funksiyaning o'zini o'zi chaqirishi.
  • Har rekursiv funksiyada base case (to'xtash) va rekursiv qadam (kichraytirib chaqirish) bor.
  • Chaqiruvlar steki avval o'sadi, base case'dan keyin teskari tartibda bo'shaydi.
  • To'xtash sharti yo'q yoki unga yetib bo'lmasa — RangeError: Maximum call stack size exceeded.
  • Rekursiv chaqiruv oldidagi return ni unutmang.
  • Oddiy takrorga — sikl, ichma-ich tuzilmaga — rekursiya.

Keyingi dars: IIFE va funksiya obyekt sifatida — IIFE (yozilgan zahoti chaqiriladigan funksiya) qanday ishlashini va funksiyaning name, length kabi "ichki ma'lumotlarini" o'rganamiz.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
JavaScript rekursiya: funksiya o'zini o'zi chaqirganda — IlmHamroh