Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. Qisman qo'llash: eslab olamiz
- 2.1 Ta'rif
- 2.2 Universal qisman
- 3. Currying
- 3.1 Bittalab argument
- 3.2 Qadamma-qadam
- 3.3 Currying va qisman qo'llash farqi
- 4. Universal curry
- 4.1 Har funksiyani qo'lda qayta yozmaslik
- 4.2 length tuzog'i
- 5. Argument tartibi: sozlama oldin, ma'lumot oxirida
- 5.1 Qaysi argument birinchi?
- 5.2 «Bahor» menyusi
- 5.3 Curried bosqichlar quvurda
- 6. Amaliy foydasi
- 6.1 Sozlangan funksiyalar oilasi
- 6.2 Forma tekshiruvi qoidalari
- 6.3 Hodisa tinglovchisi fabrikasi
- 7. Qachon currying kerak emas
- 8. Ko'p uchraydigan xatolar
- 8.1 Qo'lda curried funksiyani bir chaqiruvda chaqirish
- 8.2 Oxirgi qavsni unutish
- 8.3 Noto'g'ri argument tartibi
- 8.4 curry va standart qiymat
- 9. Mashqlar
- 1-mashq (oson): Ko'paytiruvchi
- 2-mashq (o'rta): Taksi narxi
- 3-mashq (qiyin): pipe + curried bosqichlar
- 4-mashq: Vazifalar qadami — curried filtr predikati
- 10. Real ishda
- Xulosa
- Manbalar
JavaScript currying va partial application: argumentlarni bittalab berish
Qisqacha: Currying — ko'p argumentli funksiyani bittadan argument oladigan funksiyalar zanjiriga aylantirish:
chegirma(10, 35000)o'rnigachegirma(10)(35000). Partial application (qisman qo'llash) — argumentlarning bir qismini oldindan berib, qolganini kutadigan yangi funksiya olish:const onFoiz = chegirma(10). Ikkalasining foydasi bir: sozlangan, bir argumentli funksiyalarmap,filtervapipega to'g'ridan-to'g'ri sig'adi.
Bu darsda
- Currying va qisman qo'llash nima ekanini va ular bir-biridan qanday farq qilishini tushuntira olasiz.
- Curried funksiyani arrow'lar zanjiri bilan qo'lda yozasiz.
- Istalgan funksiyani curried qiladigan universal
curryni o'zingiz yozasiz valengthtuzog'ini bilasiz. - Argumentlar tartibini "sozlama oldin, ma'lumot oxirida" qoidasi bilan tanlaysiz.
- Curried funksiyalarni
filter,mapvapipebilan birga ishlatasiz.
Oldin bilishingiz kerak: Kompozitsiya: compose va pipe, call, apply, bind, Closure, Closure amaliyotda.
1. Nega bu kerak?
O'tgan darsning «Ko'p uchraydigan xatolar» bo'limida bir muammo ochiq qolgan edi. chegirma(foiz, narx) ikki argument oladi, quvur esa har bosqichga bittasini beradi:
const chegirma = (foiz, narx) => narx - (narx * foiz) / 100;
console.log([35000, 28000].map(chegirma)); // [ 0, -279 ]map birinchi argumentga narxni, ikkinchisiga indeksni berdi. chegirma(35000, 0) — "35 000 foiz chegirma, narx 0" degani bo'lib qoldi. Ikkinchisi esa "28 000 foiz, narx 1" — manfiy narx. Natija ma'nosiz.
Yechimni bilamiz — arrow bilan o'rash: map((narx) => chegirma(10, narx)). Lekin «Bahor» kodida bunday o'ramlar ko'payib ketdi:
taomlar.filter((t) => t.narx < 30000);
taomlar.filter((t) => t.narx < 10000);
taomlar.filter((t) => t.turkum === "ichimlik");
taomlar.filter((t) => t.turkum === "suyuq");Har qatorda bir xil fikr — "narxi N dan arzon", "turkumi X" — faqat son yoki so'z o'zgaradi. Shunday yozsak qanday bo'lardi?
taomlar.filter(arzonmi(30000));
taomlar.filter(turkummi("ichimlik"));arzonmi(30000) — "30 000 dan arzonmi?" degan tayyor savol. Uni filter ga to'g'ridan-to'g'ri berasiz. Buning uchun funksiya argumentlarni bittalab qabul qilishi kerak: avval chegarani, keyin taomni. Bugungi dars shu haqida.
2. Qisman qo'llash: eslab olamiz
2.1 Ta'rif
call, apply, bind darsida qisman qo'llash (partial application) bilan tanishgan edik: funksiyaning bir qism argumentlarini hozir berib, qolganini keyinga qoldirish. Choyxonadagi "har doimgidek" o'xshatishini eslang: choy turi va shakar oldindan ma'lum, siz faqat nechta piyola ekanini aytasiz.
Ikki yo'li bor edi:
const chegirma = (foiz, narx) => narx - (narx * foiz) / 100;
const onFoizBind = chegirma.bind(null, 10);
const onFoizArrow = (narx) => chegirma(10, narx);
console.log(onFoizBind(35000)); // 31500
console.log(onFoizArrow(35000)); // 31500
console.log([35000, 28000].map(onFoizArrow)); // [ 31500, 25200 ]Ikkalasi ham bitta argument kutadigan yangi funksiya qaytardi. Endi u map ga bemalol sig'adi.
2.2 Universal qisman
O'sha darsda bind ning ichini o'zimiz yozgan edik (bogla). this bizga kerak bo'lmasa, u yanada soddalashadi:
const qisman = (funksiya, ...oldingi) =>
(...keyingi) => funksiya(...oldingi, ...keyingi);
const chekYoz = (nom, soni, narx) =>
`${nom} x${soni}: ${soni * narx}`;
const oshChek = qisman(chekYoz, "Osh");
const ikkiOsh = qisman(chekYoz, "Osh", 2);
console.log(oshChek(3, 35000)); // Osh x3: 105000
console.log(ikkiOsh(35000)); // Osh x2: 70000Qisman qo'llashning asosiy belgisi: argumentlar bir yoki bir necha bo'lib beriladi, lekin ikki bosqichda. Oldindan bergansiz — qolganini bitta chaqiruvda berasiz va natija darhol chiqadi.
3. Currying
3.1 Bittalab argument
Currying — ko'p argumentli funksiyani bittadan argument oladigan funksiyalar zanjiriga aylantirish. Nomi matematik Haskell Curry sharafiga qo'yilgan. O'zbekchada maxsus nomi yo'q, dasturchilar "currying" va "curried funksiya" deyishadi.
O'xshatish: taksi ilovasida buyurtma. Avval manzilni tanlaysiz, keyin tarifni, keyin to'lov turini. Har qadamda bitta tanlov, va har qadamdan keyin ilova keyingi savolni beradi. Uchinchi tanlovdan keyingina mashina chaqiriladi.
Curried funksiyani arrow'lar bilan qo'lda yozish juda qisqa:
const chegirma = (foiz) => (narx) => narx - (narx * foiz) / 100;
console.log(chegirma(10)(35000)); // 31500
const onFoiz = chegirma(10);
console.log([35000, 28000].map(onFoiz)); // [ 31500, 25200 ](foiz) => (narx) => ... ni o'ngdan o'qing: "foizni oladi va funksiya qaytaradi; u narxni oladi va natijani qaytaradi". Ikki strelka — ikki chaqiruv: chegirma(10)(35000). Birinchi qavs foizni beradi, ikkinchisi narxni.
chegirma(10) ni alohida saqlasangiz — bu qisman qo'llash bo'ldi. Curried funksiyada u bepul: bind ham, o'ram ham kerak emas.
O'zingiz hisoblang: chegirma(20)(50000) natijasi — .
3.2 Qadamma-qadam
Curried funksiya closure ustiga qurilgan. Har chaqiruv bitta argumentni "cho'ntakka" soladi va keyingi funksiyani qaytaradi. Uch argumentli misolda buni kuzating — har qadamda yangi o'zgaruvchi qanday qo'shilishiga qarang:
Diqqat qiling: chekYoz("Osh") va oshUchun(2) hech narsa hisoblamadi. Ular faqat argumentni eslab, funksiya qaytardi. Haqiqiy ish faqat oxirgi argument kelganda bajarildi.
3.3 Currying va qisman qo'llash farqi
Ikki atama ko'pincha aralashtiriladi. Farqi:
| Currying | Qisman qo'llash | |
|---|---|---|
| Nima | funksiyani qayta tuzish | argumentni oldindan berish |
| Natija | f(a)(b)(c) zanjiri |
qolganini kutuvchi funksiya |
| Har chaqiruvda | bitta argument | istalgancha |
Bir gap bilan: currying — funksiyaning shakli, qisman qo'llash — uni ishlatish usuli. Curried funksiya qisman qo'llashni juda qulay qiladi: chegirma(10) — tayyor qisman qo'llangan funksiya.
flowchart LR
A["chegirma(foiz, narx)"] -- "currying" --> B["chegirma(foiz)(narx)"]
B -- "chegirma(10)" --> C["onFoiz(narx)"]
A -- "bind / qisman" --> C
C -- "onFoiz(35000)" --> D["31500"]Diagrammada ikki yo'l bir joyga — bir argumentli onFoiz ga olib keladi. Yuqoridagisi avval funksiyani curried qiladi, pastdagisi argumentni to'g'ridan-to'g'ri bog'laydi.
Tekshirib ko'ring:
const qosh = (a) => (b) => a + b;—qosh(2)nima qaytaradi?qosh(2)(3)-chi?
Javob
qosh(2) — funksiya: b ni kutadigan, a = 2 ni eslab qolgan arrow. Konsolda [Function (anonymous)] ko'rinadi. qosh(2)(3) — 5: ikkinchi qavs o'sha funksiyani 3 bilan chaqirdi.
4. Universal curry
4.1 Har funksiyani qo'lda qayta yozmaslik
Har funksiyani (a) => (b) => ... qilib qayta yozish noqulay. Ayniqsa, funksiya allaqachon yozilgan bo'lsa yoki uni oddiy usulda ham chaqirish kerak bo'lsa. Shuning uchun istalgan funksiyani curried qiladigan yordamchi yoziladi.
U qanday biladi, qachon to'xtash kerak? Funksiyaning length xususiyatidan — u nechta parametr e'lon qilinganini aytadi. Buni call, apply, bind darsida bind misolida ko'rgan edik:
function curry(funksiya, soni = funksiya.length) {
return function yigish(...berilgan) {
if (berilgan.length >= soni) {
return funksiya(...berilgan);
}
return (...keyingi) => yigish(...berilgan, ...keyingi);
};
}
const chekYoz = curry(
(nom, soni, narx) => `${nom} x${soni}: ${soni * narx}`,
);
console.log(chekYoz("Osh")(2)(35000)); // Osh x2: 70000
console.log(chekYoz("Osh", 2)(35000)); // Osh x2: 70000
console.log(chekYoz("Osh", 2, 35000)); // Osh x2: 70000Kodni qatorma-qator o'qiymiz:
soni = funksiya.length— nechta argument kerakligi. Standart qiymat: funksiyaning parametrlar soni (bu yerda 3).return function yigish(...berilgan)—curryyangi funksiya qaytaradi. Ungayigishnomi berilgan, chunki u keyin o'zini shu nom bilan chaqiradi.berilgan.length >= soni— argumentlar yetarlimi? Ha bo'lsa — asl funksiya hamma argument bilan chaqiriladi va natija qaytadi.- Yetmasa — yana bitta arrow qaytadi. U keyingi argumentlarni oladi va
yigishni eski va yangi argumentlar birgalikda bilan qayta chaqiradi.
Ya'ni yigish argumentlarni yig'adi. Yig'ilganlari closure da saqlanadi. yigish o'zini uzunroq ro'yxat bilan chaqiradi — bu rekursiya. To'xtash sharti — 3-qadam.
Qadamlarda berilgan ro'yxati qanday to'lishini kuzating:
E'tibor bering: bunday curry dan keyin funksiyani har qanday bo'laklarda chaqirsa bo'ladi — bittalab ham, ikkita-bitta ham, hammasi birdan ham. Lodash va Ramda'dagi curry ham shunday ishlaydi.
4.2 length tuzog'i
curry length ga ishonadi. length esa har doim ham haqiqiy argument sonini bermaydi:
const a = (x, y) => 0;
const b = (x, y = 10) => 0;
const c = (...sonlar) => 0;
console.log(a.length, b.length, c.length); // 2 1 0Standart qiymatli parametr va undan keyingilari, shuningdek rest parametr length ga kirmaydi. Bunday funksiyani curry qilsangiz, u kutganingizdan erta "yonadi":
function curry(funksiya, soni = funksiya.length) {
return function yigish(...berilgan) {
if (berilgan.length >= soni) {
return funksiya(...berilgan);
}
return (...keyingi) => yigish(...berilgan, ...keyingi);
};
}
const xizmatli = curry(
(narx, foiz = 10) => narx + (narx * foiz) / 100,
);
console.log(xizmatli(35000)); // 38500
const jamla = curry((...sonlar) =>
sonlar.reduce((s, x) => s + x, 0),
);
console.log(jamla(1)); // 1
const jamlaUch = curry(
(...sonlar) => sonlar.reduce((s, x) => s + x, 0),
3,
);
console.log(jamlaUch(1)(2)(3)); // 6xizmatli ning length i 1, shuning uchun u birinchi argumentdayoq bajarildi — bu yerda bu hatto qulay. jamla esa length 0 bo'lgani uchun darhol ishladi va 1 qaytardi. Yechim — sonni aniq berish: curry(funksiya, 3). Bizning curry ning ikkinchi parametri shu uchun bor.
Tekshirib ko'ring:
curry((a, b, c) => a + b + c)bilan yasalganfuchunf(1)(2)nima qaytaradi?
Javob
Funksiya. Ikki argument yig'ildi, uchtasi kerak — yigish yana bitta argument kutadigan funksiya qaytaradi. f(1)(2)(3) yoki f(1)(2, 3) — 6.
5. Argument tartibi: sozlama oldin, ma'lumot oxirida
5.1 Qaysi argument birinchi?
Currying faqat boshdagi argumentlarni oldindan berishga imkon beradi. Shuning uchun argumentlar tartibi hal qiluvchi. Ikki variantni solishtiring:
const arzonmiA = (taom) => (chegara) => taom.narx < chegara;
const arzonmiB = (chegara) => (taom) => taom.narx < chegara;arzonmiA(taom) — "shu taom uchun chegara so'ra". Bu bilan filter ga hech narsa berib bo'lmaydi: bizga taom oldindan ma'lum emas, u filter ichida keladi. arzonmiB(30000) esa — "30 000 chegarasi bilan taom so'ra". Mana shu filter ga sig'adi.
Qoida: sozlama oldin, ma'lumot oxirida. Kamroq o'zgaradigan argument — chegara, foiz, turkum — birinchi. Har chaqiruvda o'zgaradigan asosiy ma'lumot — taom, narx, ro'yxat — oxirgi. Inglizcha "data-last" deyiladi. Ramda kutubxonasidagi hamma funksiyalar shu tartibda yozilgan.
5.2 «Bahor» menyusi
Endi «Nega bu kerak?» bo'limidagi orzuni amalga oshiramiz:
const arzonmi = (chegara) => (taom) => taom.narx < chegara;
const turkummi = (turkum) => (taom) => taom.turkum === turkum;
const taomlar = [
{ nom: "Osh", narx: 35000, turkum: "milliy" },
{ nom: "Manti", narx: 30000, turkum: "milliy" },
{ nom: "Ko'k choy", narx: 5000, turkum: "ichimlik" },
{ nom: "Kompot", narx: 8000, turkum: "ichimlik" },
];
const nomi = (taom) => taom.nom;
console.log(taomlar.filter(arzonmi(10000)).map(nomi));
console.log(taomlar.filter(turkummi("milliy")).map(nomi));Konsolda:
[ "Ko'k choy", 'Kompot' ]
[ 'Osh', 'Manti' ]filter(arzonmi(10000)) gap kabi o'qiladi: "10 000 dan arzonlarini tanla". Predikat nomi kurs qoidasiga mos — "-mi" bilan. Chegara o'zgarsa, faqat son o'zgaradi.
5.3 Curried bosqichlar quvurda
Currying va pipe bir-birini to'ldiradi. Massiv metodlarini ham curried qilsak, ular quvurning bosqichiga aylanadi:
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const filtrla = (shart) => (royxat) => royxat.filter(shart);
const aylantir = (fn) => (royxat) => royxat.map(fn);
const chegirma = (foiz) => (narx) => narx - (narx * foiz) / 100;
const ichimlikNarxlari = pipe(
filtrla((t) => t.turkum === "ichimlik"),
aylantir((t) => t.narx),
aylantir(chegirma(20)),
);
const taomlar = [
{ nom: "Osh", narx: 35000, turkum: "milliy" },
{ nom: "Ko'k choy", narx: 5000, turkum: "ichimlik" },
{ nom: "Kompot", narx: 8000, turkum: "ichimlik" },
];
console.log(ichimlikNarxlari(taomlar)); // [ 4000, 6400 ]filtrla — filter ning curried, data-last nusxasi: avval shart, oxirida ro'yxat. Quvur har bosqichga ro'yxatni beradi, shart esa oldindan "yopishtirilgan". O'tgan darsdagi ikki argumentli funksiya muammosi shu tarzda hal bo'ladi.
Tekshirib ko'ring: Nega JavaScript'ning o'z
royxat.filter(shart)metodi bilan "data-last" haqida gapirish ma'nosiz?
Javob
Chunki metodda ma'lumot — royxat — argument emas, nuqtadan oldingi obyekt. U har doim "birinchi" keladi. Currying va data-last tartib oddiy funksiyalar uchun; metodni quvurga qo'yish uchun uni filtrla kabi funksiyaga o'raymiz.
6. Amaliy foydasi
6.1 Sozlangan funksiyalar oilasi
Bitta umumiy funksiyadan bir nechta aniq funksiya yasash — currying'ning eng ko'p ishlatiladigan joyi. Masalan, log yozuvchi:
const logYoz = (daraja) => (manba) => (xabar) =>
console.log(`[${daraja}] ${manba}: ${xabar}`);
const bronXatosi = logYoz("XATO")("bron");
const bronHaqida = logYoz("INFO")("bron");
bronHaqida("Dilshod aka, 4 kishi, 19:00");
bronXatosi("Soat 23:30 — ish vaqtidan tashqari");Konsolda:
[INFO] bron: Dilshod aka, 4 kishi, 19:00
[XATO] bron: Soat 23:30 — ish vaqtidan tashqariDaraja va manba bir marta beriladi. Kodning qolgan qismi faqat bronXatosi("...") deb yozadi — qisqa va xato qilish qiyin.
6.2 Forma tekshiruvi qoidalari
Xato strategiyasi darsidagi bron tekshiruvini eslang. Qoidalarni curried funksiyalar bilan yozsak, ular qayta ishlatiladigan "g'ishtlar"ga aylanadi:
const kamida = (soni) => (matn) => matn.trim().length >= soni;
const oraliqda = (min, max) => (son) => son >= min && son <= max;
const ismTogrimi = kamida(2);
const odamTogrimi = oraliqda(1, 20);
console.log(ismTogrimi("Ali")); // true
console.log(ismTogrimi(" A ")); // false
console.log(odamTogrimi(25)); // falseoraliqda(1, 20) ga e'tibor bering: birinchi chaqiruv ikkita argument oldi. Currying qat'iy qoida emas — sozlamalar bir guruh bo'lsa, ularni birga berish mumkin. Muhimi — ma'lumot (son) alohida va oxirida.
6.3 Hodisa tinglovchisi fabrikasi
Brauzerda ham currying uchraydi. Bir nechta tugmaga deyarli bir xil tinglovchi kerak bo'lsa:
// faolTugma, hammasiTugma — sahifadagi ikki tugma
const filtrniTanla = (filtr) => (event) => {
event.preventDefault();
console.log(`Filtr: ${filtr}`);
};
faolTugma.addEventListener("click", filtrniTanla("faol"));
hammasiTugma.addEventListener("click", filtrniTanla("hammasi"));filtrniTanla("faol") — hodisani kutadigan tayyor tinglovchi. Bu Closure amaliyotda darsidagi fabrika funksiyaning o'zi, faqat endi unga nom berdik — curried funksiya.
7. Qachon currying kerak emas
Currying — vosita, maqsad emas. Uni hamma joyga qo'yish kodni og'irlashtiradi:
- Bir marta ishlatiladigan funksiya. Agar funksiya doim hamma argumentlari bilan bir joyda chaqirilsa,
f(a)(b)(c)faqat qavslarni ko'paytiradi. - Ko'p, teng huquqli argumentlar. Beshta parametrli funksiyani bittalab chaqirish o'qishga qiyin. Bunday holatda bitta sozlamalar obyekti (
{ narx, foiz, valyuta }) yaxshiroq (Parametrda destructuring va options obyekti). - Jamoa tanish bo'lmasa.
logYoz("XATO")("bron")("...")ni birinchi ko'rgan dasturchi hayron qoladi. Yangi jamoada avval kelishib oling. - Stek izi. Xato chiqqanda stek izida (Stack trace) nomsiz arrow'lar zanjiri ko'rinadi. Nomli qadamlar bilan yozilgan kodni tuzatish osonroq.
Tezlik haqida qayg'urmang: har chaqiruv kichik funksiya yaratadi, lekin odatdagi ilovada bu sezilmaydi. Muammo bo'lsa — o'lchab ko'ring (Performansni o'lchash darsida).
8. Ko'p uchraydigan xatolar
8.1 Qo'lda curried funksiyani bir chaqiruvda chaqirish
const chegirma = (foiz) => (narx) => narx - (narx * foiz) / 100;
console.log(chegirma(10, 35000)); // [Function (anonymous)]Xato xabari yo'q — faqat natija o'rnida funksiya. Qo'lda yozilgan curried funksiya ikkinchi argumentni jimgina tashlab yuboradi. Tuzatish: chegirma(10)(35000). Yoki curry yordamchisi bilan yasang — u ikkala usulni ham qabul qiladi.
8.2 Oxirgi qavsni unutish
const narx = chegirma(10); deb yozib, narx ni son deb o'ylash. Keyin unga son qo'shilsa, g'alati satr chiqadi:
const chegirma = (foiz) => (narx) => narx - (narx * foiz) / 100;
const narx = chegirma(10);
console.log(narx + 1000); // (narx) => narx - (narx * foiz) / 1001000G'alati ko'rinadi, keling sekin ko'ramiz. narx — son emas, funksiya. + funksiyani satrga — o'zining kod matniga — aylantirdi: (narx) => narx - (narx * foiz) / 100. Keyin oxiriga "1000" yopishtirdi. Natijada matn oxiridagi 100 va 1000 qo'shilib, 1001000 bo'lib ko'rindi. Tuzatish: natija o'rnida [Function ...] yoki funksiya matni ko'rsangiz, qaysidir qavs yetishmayapti.
8.3 Noto'g'ri argument tartibi
Ma'lumotni birinchi qo'yish: (taom) => (chegara) => .... Bunday funksiyani filter ga oldindan sozlab bo'lmaydi. Tuzatish: sozlama oldin, ma'lumot oxirida.
8.4 curry va standart qiymat
curry((narx, foiz = 10) => ...) dan xizmatli(35000)(20) kutish. Funksiya birinchi argumentdayoq bajarildi va son qaytardi. Ikkinchi qavs esa sonni chaqirishga urinadi:
function curry(funksiya, soni = funksiya.length) {
return function yigish(...berilgan) {
if (berilgan.length >= soni) {
return funksiya(...berilgan);
}
return (...keyingi) => yigish(...berilgan, ...keyingi);
};
}
const xizmatli = curry(
(narx, foiz = 10) => narx + (narx * foiz) / 100,
);
console.log(xizmatli(35000)(20));TypeError: xizmatli(...) is not a functionTarjimasi: "xizmatli(...) funksiya emas." xizmatli(35000) allaqachon 38500 sonini qaytardi, uni (20) bilan chaqirib bo'lmaydi. Tuzatish: sonni aniq bering — curry(fn, 2) — yoki standart qiymatdan voz keching.
9. Mashqlar
1-mashq (oson): Ko'paytiruvchi
Curried kopaytir funksiyasini qo'lda (arrow'lar bilan) yozing. Undan ikkiBaravar va uchBaravar funksiyalarini yasang va [1, 2, 3] massiviga map bilan qo'llang.
Yechim
const kopaytir = (a) => (b) => a * b;
const ikkiBaravar = kopaytir(2);
const uchBaravar = kopaytir(3);
console.log([1, 2, 3].map(ikkiBaravar)); // [ 2, 4, 6 ]
console.log([1, 2, 3].map(uchBaravar)); // [ 3, 6, 9 ]kopaytir(2) — a = 2 ni eslab qolgan funksiya. map unga elementni beradi va ko'paytma qaytadi. map ning ortiqcha argumentlari (indeks, massiv) zarar qilmaydi: ichki arrow faqat bitta parametr oladi.
2-mashq (o'rta): Taksi narxi
Taksi narxi: boshlangich + km * kmNarxi. taksiNarxi ni universal curry bilan yasang (curry ni darsdan ko'chiring). Undan ekonom (boshlang'ich 5 000, km uchun 2 000) va komfort (8 000 va 3 000) tariflarini qisman qo'llab, 4 km va 10 km safarlar narxini chiqaring. Argument tartibi: avval tarif sozlamalari, oxirida masofa.
Yechim
function curry(funksiya, soni = funksiya.length) {
return function yigish(...berilgan) {
if (berilgan.length >= soni) {
return funksiya(...berilgan);
}
return (...keyingi) => yigish(...berilgan, ...keyingi);
};
}
const taksiNarxi = curry(
(boshlangich, kmNarxi, km) => boshlangich + km * kmNarxi,
);
const ekonom = taksiNarxi(5000, 2000);
const komfort = taksiNarxi(8000)(3000);
console.log([4, 10].map(ekonom)); // [ 13000, 25000 ]
console.log([4, 10].map(komfort)); // [ 20000, 38000 ]ekonom ikki sozlamani bitta chaqiruvda oldi, komfort — ikki chaqiruvda. Universal curry ikkalasini ham qabul qiladi. Masofa oxirida bo'lgani uchun tarif funksiyalari map ga to'g'ridan-to'g'ri sig'di.
3-mashq (qiyin): pipe + curried bosqichlar
Darsdagi pipe, filtrla va aylantir ni ishlating. Yana bitta curried bosqich yozing: saralash(kalit) — ro'yxatni kalit maydoni bo'yicha o'sish tartibida, nusxa bilan (toSorted) saralaydi. Quvur: milliy taomlarni tanlash → narx bo'yicha saralash → "Nom — narx" satrlariga aylantirish.
Yechim
const pipe = (...bosqichlar) => (kirish) =>
bosqichlar.reduce((natija, bosqich) => bosqich(natija), kirish);
const filtrla = (shart) => (royxat) => royxat.filter(shart);
const aylantir = (fn) => (royxat) => royxat.map(fn);
const saralash = (kalit) => (royxat) =>
royxat.toSorted((a, b) => a[kalit] - b[kalit]);
const milliyMenyu = pipe(
filtrla((t) => t.turkum === "milliy"),
saralash("narx"),
aylantir((t) => `${t.nom} — ${t.narx}`),
);
const taomlar = [
{ nom: "Osh", narx: 35000, turkum: "milliy" },
{ nom: "Ko'k choy", narx: 5000, turkum: "ichimlik" },
{ nom: "Lag'mon", narx: 28000, turkum: "milliy" },
{ nom: "Manti", narx: 30000, turkum: "milliy" },
];
console.log(milliyMenyu(taomlar));Konsolda:
[ "Lag'mon — 28000", 'Manti — 30000', 'Osh — 35000' ]saralash("narx") — kalitni eslab qolgan bosqich. toSorted asl ro'yxatni o'zgartirmaydi, shuning uchun bosqich toza. a[kalit] — kvadrat qavs bilan maydonni nomi bo'yicha o'qish (Obyekt: kalit-qiymat juftliklari).
4-mashq: Vazifalar qadami — curried filtr predikati
vazifalar dagi VazifalarRoyxati.mosmi(vazifa, filtr) ikki argument oladi, shuning uchun korinadiganlar ichida arrow o'rami bor: filter((v) => VazifalarRoyxati.mosmi(v, filtr)). Kanon kodni o'zgartirmaymiz. kurs/mashqlar/12/25-curry/filtr.mjs faylida curried filtrgaMos(filtr) yozing. U mosmi bilan bir xil qoidaga amal qilsin, lekin filter ga to'g'ridan-to'g'ri berilsin. Uch filtrni sinang.
Yechim
const filtrgaMos = (filtr) => (vazifa) => {
if (filtr === "faol") {
return !vazifa.bajarildi;
}
if (filtr === "bajarilgan") {
return vazifa.bajarildi;
}
return true;
};
const vazifalar = [
{ id: 1, matn: "Non olish", bajarildi: false },
{ id: 2, matn: "DOM darsini takrorlash", bajarildi: true },
{ id: 3, matn: "Onamga qo'ng'iroq qilish", bajarildi: false },
];
for (const filtr of ["hammasi", "faol", "bajarilgan"]) {
const idlar = vazifalar.filter(filtrgaMos(filtr)).map((v) => v.id);
console.log(filtr, idlar);
}Konsolda:
hammasi [ 1, 2, 3 ]
faol [ 1, 3 ]
bajarilgan [ 2 ]Argument tartibi teskari bo'ldi: kanon mosmi(vazifa, filtr) da ma'lumot birinchi, bizda esa sozlama — filtr — birinchi. Shuning uchun filter(filtrgaMos(filtr)) o'ramsiz yozildi. Kanon nomlarni o'zgartirish — 12-qism oxiridagi refaktor darslarining ishi; hozircha bu faqat mashq.
git add 12/25-curry/filtr.mjs
git commit -m "12/25: curried filtr predikati (data-last)"10. Real ishda
- Ramda va lodash/fp. Ramda'da hamma funksiya avtomatik curried va data-last:
R.filter(R.propEq("milliy", "turkum")). Lodash'ninglodash/fpversiyasi ham shunday. Lodash'ning oddiy versiyasida esa_.curryva_.partialbor. - React. Ro'yxatdagi har element uchun tinglovchi:
onClick={ochir(vazifa.id)}—ochir = (id) => () => .... 17-qismda ko'p uchratasiz. - Redux middleware. Redux'dagi middleware imzosi —
store => next => action => ...— uch qavatli curried funksiya. Uni ko'rganingizda endi qo'rqmaysiz. - Sozlanadigan yordamchilar. Log yozuvchilar, validatsiya qoidalari, narx formatlovchilar — "bir marta sozlab, ko'p ishlat" kerak bo'lgan hamma joyda.
- Intervyu. "
curryfunksiyasini yozing", "sum(1)(2)(3)qanday ishlaydi?", "currying va partial application farqi?" — frontend intervyularida tez-tez beriladi.
Xulosa
- Currying —
f(a, b, c)nif(a)(b)(c)ga aylantirish; har chaqiruv bitta argumentni closure'da eslab, keyingi funksiyani qaytaradi. - Qisman qo'llash — argumentlarning bir qismini oldindan berish;
bind, arrow o'ram yoki curried funksiyaning birinchi chaqiruvi bilan. - Qo'lda:
(foiz) => (narx) => ...; universalcurry—lengthbo'yicha argument yig'adi, standart qiymat va rest bilan sonni aniq bering. - Tartib: sozlama oldin, ma'lumot oxirida — shunda
filter(arzonmi(10000))vapipebosqichlari o'ramsiz yoziladi. - Foydasi: sozlangan funksiyalar oilasi, validatsiya qoidalari, tinglovchi fabrikasi; ortiqcha ishlatilsa — kod og'irlashadi.
Keyingi dars: Memoization — toza funksiya natijasini keshlab, og'ir hisobni qayta qilmaslikni va keshni cheklashni o'rganamiz.
Manbalar
- MDN: "Function.prototype.bind", "Function: length", "Closures" — developer.mozilla.org
- Ramda hujjatlari:
curry,filter— ramdajs.com/docs - Lodash hujjatlari:
_.curry,_.partial— lodash.com/docs - Redux hujjatlari: "Middleware" — redux.js.org/understanding/history-and-design/middleware
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!