Funksiya tushunchasi
Ma’lumki, Arduinoda har qanday skechda 2 ta majburiy funksiya mavjud bo’ladi: “setup()” va “loop()” funksiyalari. Lekin bulardan tashqari, skechda foydalanuvchi o’z funksiyalaridan hamda biblioteka funksiyalaridan ham foydalanishi mumkin. Biblioteka funksiyalaridan foydalanish ham foydalanuvchi funksiyalariga o’xshash tarzda amalga oshiriladi. Lekin, biblioteka funksiyalari asosiy sketchdan tashqari fayllarda hosil qilinganligi sababli, bunday funksiyalardan foydalanishdan oldin sketchning boshlang’ich sohasida mazkur fayllar (bibliotekalar)ni “#include” operatori yordamida qo’shib olish talab etiladi.
Quyida Arduino IDE da (C++ dasturlash tilida) qo’llaniladigan funksiyalarni shartli ravishda bir necha turlarga ajratamiz va ular bilan tanishamiz.
Funksiya – bu dasturning alohida nomga ega bo’lgan qismi.
Katta xajmli dastur bilan ishlashni soddalashtirish uchun uni funksiyalarga ajratish maqsadga muvofiq. Bundan tashqari, dastur davomida ko’p marta takrorlanadigan kodlar majmuasini, alohida funksiyaga ajratib olish mumkin. Natijada funksiyani qayta-qayta ishga tushirish (chaqirish) uchun dastur davomida faqat uning nomini ko’rsatish yetarli bo’ladi. Funksiyadan foydalanishning yana bir afzalligi, ko’p foydalaniladigan funksiya xotiradan faqat 1 marta joy egallashi sababli, qurilmaning flesh-xotirasini iqtisod qilishga ham yordam beradi.
Funksiya boshqa funksiyalardan alohida holda keltirilishi kerak. Shundan keyin uni nomi bo’yicha chaqirish mumkin bo’ladi. Funksiya quyidagi tuzilishga ega bo’ladi:
qiymat_turi funksiya_nomi (argumentlar) {
funksiya_kodi
Bu yerda:
qiymat_turi – funksiya natijasida o’zlashtiriladigan qiymatning turi, funksiya_nomi – funksiyani chaqirish uchun ishlatiladigan nom, argumentlar – funksiya ichida qo’llaniladigan o’zgaruvchilar ro’yxati (qo’llanilishi majburiy emas), funksiya_kodi – funksiya ichida bajariladigan amallar ketma-ketligi.
Agar funksiya argumentlarga ega bo’lmasa, uni chaqirishda “()” (qavs) belgilarini ham tushirib qoldirish munkin (Lekin, funksiyani e’lon qilishda, argumentga ega bo’lmagan taqdirda ham, () belgilari ko’rsatilishi shart).
Agar funksiya argumentlarga ega bo’lsa, uni chaqirish quyidagicha bajariladi:
funksiya_nomi(1-argument, 2-argument, …);
Funksiya har doim natijani o’zida saqlaydi. Agar funksiya bajarilishi natijasini biron o’zgaruvchiga o’zlashtirish talab qilinsa, funksiyani chaqirish quyidagi tuzilishga ega bo’lishi kerak (argumentga ega bo’lishi yoki ega emasligidan qat’iy nazar):
natija = funksiya_nomi();
Funksiyaning asosiy turlari
Funksiya argument qabul qilishi yoki qabul qilmasligi, shuningdek uning natijasi o’zgaruvchiga o’zlashtirlishi yoki o’zlashtirilmasligi mumkin. Shulardan kelib chiqib, funksiyani 4 ta asosiy turlarga bo’lish mumkin. Quyida ularning har birini misol asosida ko’rib chiqamiz:
1) Argument qabul qilmaydigan va natijani qaytarmaydigan funksiya (ayrim boshqa dasturlash turlarida bunday funksiyani protsedura deb ataladi).
Bunday funksiyani e’lon qilishda “void” (“bo’shliq”) deb nomlangan qiymat turi qo’llaniladi. Bizga ma’lum bo’lgan “setup” va “loop” majburiy funksiyalari ham ushbu turga mansub.
Misol tariqasida 2 ta sonni yig’indisini hisoblab, natijani o’zgaruvchiga o’zlashtiradigan funksiyani ko’rib chiqamiz.
Bunday turdagi funksiyadan foydalanish va uni tushunish juda oson, lekin optimallik nuqtai nazaridan qaraganda qo’pol hisoblanadi. Chunki, bunday usulda o’zgaruvchilardan funksiya ichida va tashqarisida foydalanish uchun ularni global o’zgaruvchi sifatida e’lon qilishga to’g’ri keladi, chunki bunday funksiya yuqorida aytilganidek, natijani qaytarmaydi. Shuning uchun, funksiya natijasini, global o’zlashtirishga to’g’ri keladi. Global o’zgaruvchilar esa, qurilma tezkor (SRAM) xotirasidan joy band qiladi (bu joy qurilma ishi davomida doim band turadi). Bunday funksiyadan foydalanish, ayniqsa tezkor xotirasi xajmi kichik bo’lgan mikrokontrollerlar uchun to’g’ri kelmasligi mumkin.
2) Argument qabul qilmaydigan lekin natijani qaytaradigan funksiya.
Natijani qaytaradigan funksiyani e’lon qilish uchun, uning qaytaradigan natijasi qiymati turi oldindan ma’lum bo’lishi kerak. Aks holda, dasturda xatolik yuz berishi mumkin.
Masalan, yuqoridagi misoldagi a va b o’zgaruvchilari qiymati byte (0 dan 255 gacha qiymat qabul qiladi) turiga mansub, c esa, int (-32768 dan 32768 gacha) turiga mansub. Agar c ham byte turiga mansub bo’lganida hamda a va b o’zgaruvchilarining qiymatlari yig’indisi 255 dan ortib ketganida, xatolik yuz bergan bo’lar edi. Shuning uchun ham c o’zgaruvchisi uchun int turi qo’llanildi.
Ushbu funksiya turida, natija qaytarilishi uchun, funksiya oxirida return operatoridan foydalaniladi. Ushbu operator nafaqat natijani qaytaradi, balki funksiya bajarilishini ham yakunlaydi (return operatoridan, yuqoridagi void turidagi funksiyani yakunlash uchun ham foydalanish mumkin, biroq bunda funksiya turidan kelib chiqib, natija baribir qaytarilmaydi).
Yuqoridagi misolni, natijani qaytaradigan funksiya uchun o’zgartiramiz:
Ko’rinib turibdiki, ushbu misolda c o’zgaruvchisi “setup” funksiyasi ichida lokal o’zgaruvchi sifatida e’lon qilingan. Funksiya ichida e’lon qilingan o’zgaruvchidan boshqa funksiyalarda foydalanib bolmaydi, lekin u tezkor xotiradan ham doimiy joy talab qilmaydi. Aniqrogi, funksiya ishi yakunlanganidan keyin, xotiradan o’chib ketadi. Lekin, agar c o’zgaruvchisining qiymatidan boshqa funksiyalarda ham foydalanish zarur bo’lsa, uni ham global o’zgaruvchi sifatida e’lon qilinishi kerak.
3) Argumentlar qabul qiladigan va natijani ham qaytaradigan funksiya.
Ushbu funksiyani chaqirish va e’lon qilishda, ham uning argumentlari, ham qaytaradigan natija qiymati turi oldindan ma’lum bo’lishi kerak. Aks holda, dasturda xatolik yuz berishi mumkin.
Funksiyani e’lon qilishda argumentlar yangi o’zgaruvchilar yordamida, ularning qiymat turi va nomini ko’rsatgan holda vergul belgisi bilan ajratib ko’rsatiladi. Funksiya chaqirilgan paytda, uning argumentlari funksiya ichida foydalanilishi mumkin bo’lgan lokal o’zgaruvchilarga aylanadi. Ya’ni dastlabki o’zgaruvchilarning qiymati funksiya ichidagi yangi o’zgaruvchilarga nusxalanadi.
Yana, yuqoridagi misolni o’zgartiramiz:
Ko’rinib turibdiki, ushbu misolda a, b, c o’zgaruvchlarning barchasi “setup” funksiyasi ichida lokal o’zgaruvchi sifatida e’lon qilingan. Demak, kod yanada optimallashdi.
Muhim eslatma.
funksiyani chaqirishda argument sifatida o’zgaruvchi o’rniga bir yola sonlardan ham foydalanish mumkin:
c = yigindiniHisoblash(15, 25);
Lekin shuni unutmaslik kerakki, Arduinoda turi e’lon qilinmagan qiymatlar int turiga mansub deb qaraladi. Natijada, int turidagi ushbu qiymatlar funksiyaning yangi o’zgaruvchilari turi (byte) dan farq qilib qoladi va xatolik yuz berishi mumkin! Masalan, quyidagi ifoda yuqoridagi misolga tatbiq etilsa, xatolik yuz beradi:
c = yigindiniHisoblash(150, 250); //150 va 250 int turiga, a1 va b1 lar qiymati esa byte turiga mansub.
Xatolikning oldini olish uchun, funksiyani chaqirish paytida argumentlarning qiymat turini byte sifatida aniq ko’rsatish mumkin:
c = yigindiniHisoblash(byte(150), byte(250));
Funksiya universalligini saqlash uchun, yaxshisi uni e’lon qilishda ham byte o’rniga int turini ko’rsatish maqsadga muvofiq bo’ladi:
int yigindiniHisoblash(int a1, int b1) {
return (a1 + b1);
}
Yuqoridagi misollarning barchasida byte va int qiymat turlaridan foydalanildi. Agar o’zgaruvchilar qiymat turi har xil bo’lsa, dasturni tegishli ravishda o’zgartirish kerak bo’ladi:
4) Argument qabul qiladigan lekin natijani qaytarmaydigan funksiya.
Bunday funksiya ham yuqoridagi 1-turdagi funksiya bilan deyarli bir xil ishlaydi, farqi uni chaqirishda funksiya argumentlarini ham ko’rsatiladi. E’lon qilishda esa, “void” deb nomlangan qiymat turi qo’llaniladi. Shu nuqtai nazardan qaraganda, 1-funksiya ushbu 4-funksiyaning xususiy holati deb qaralishi ham mumkin.
Demak, ushbu holda a va b lokal o’zgaruvchilari funksiyaning argumenti sifatida ko’rsatilib, natija global c o’zgaruvchisi orqali qaytarilyapti. Ushbu funksiya natijani qaytarmasa-da, natija o’zlashtirilgan o’zgaruvchi global e’lon qilinganligi sababli, uning qiymatidan boshqa funksiyalarda ham foydalanilishi mumkin.
Funksiyaning boshqa ayrim turlari
C++ dasturlash tilida (Arduinoda ham) haddan tashqari yuklangan funksiya (“перегруженная функция”) deb ataladigan funksiya ham farqlanadi.
Haddan tashqari yuklangan funksiya deganda, bir xil nom bilan e’lon qilingan, lekin har xil qiymat turidagi natijalarni qaytaradigan yoki turlicha sondagi argumentlarga ega bo’lgan funksiya tushuniladi. Bir yo’la misolga o’tamiz:
Bu yerda bir xil nomli funksiya 3 xil usulda chaqirilganda 3 xil natija qaytariladi. Dastur ko’rsatilgan argumentlar soni va ularning qiymat turlariga qarab, kerakli funksiyani o’zi chaqiradi va tegishli natijani qaytaradi: argument sifatida 2 ta butun son ko’rsatilsa 1-funksiya, 3 ta butun son ko’rsatilsa 2-funksiya va float turli o’zgaruvchilar ko’ratilsa 3-funksiya bajariladi.
O’zgaruvchining qiymatiga o’zgartirish kiritadigan funksiya – bu funksiyani chaqirish paytida argument sifatida ko’rsatilgan o’zgaruvchining dastlabki qiymatini o’zgartiradigan funksiya hisoblanadi.
Oldingi funksiyalar bo’yicha misollardan ma’lumki, funksiyani e’lon qilish paytida e’lon qilingan yangi o’zgaruvchilar qiymatlari funksiyani chaqirish paytida argument sifatida ko’rsatilgan o’zgaruvchilarning qiymatlaridan nusxalangan edi. Funksiya kodidagi amallar esa faqat yangi lokal o’zgaruvchilar ustida bajarilgan. Ya’ni, oldingi o’zgaruvchilar qiymatiga ta’sir qilmagan. Navbatdagi turdagi funksiya bajarilishi natijasida aynan dastlabki o’zgaruvchi qiymati o’zgaradi. Buning uchun funksiyada dastlabki o’zgaruvchi qiymatini havola (“ссылка”) sifatida ko’rsatiladi. Natijada, funksiyaning ichida yangi o’zgaruvchi ustida bajarilgan amallar aslida dastlabki o’zgaruvchi qiymatini o’zgartiradi. Havolani ko’rsatishda funksiyani e’lon qilish paytida yangi o’zgaruvchi qiymat turi oxiriga & (ampersand) belgisi qo’shilishi yetarli. Misol:
Funksiya yordamida har xil qiymat turiga mansub o’zgaruvchilar bilan ishlash
Dastur davomida har xil turdagi o’zgaruvchilar qiymati bilan ishlash jarayonini yagona funksiyani yaratish orqali ham bajarish mumkin. Masalan, bizga int va float turidagi qiymatlar berilgan bo’lsin, 2 xil funksiya bajarilishi natijalarini (ularning yig’indisini va matn sifatida birlashtirilgan holatini) port monitoriga chiqaramiz:
Funksiyadan bir nechta natijalarni qaytarish
Umuman olganda, C++ da (Arduinoda ham) funksiyadan bir nechta natijani qaytarish tushunchasi yo’q. Lekin, struktura yordamida murakkabroq bo’lsa-da, shunday “qo’lbola” funksiyani yaratsa bo’ladi.
Struktura o’zi nima?
Struktura (struct) – har xil qiymat turlari asosidagi o’zgaruvchilarning bir nom ostida va alohida qiymat turi sifatida e’lon qilinishi hisoblanadi.
Struktura bilan ishlash quyidagi tartibda amalga oshiriladi:
1) Global o’zgaruvchilar sohasida yangi struktura turi (yorlig’i) va uning tarkibidagi o’zgaruvchilar e’lon qilinadi;
2) E’lon qilingan struktura turi asosida funksiya yaratiladi (turlarini ko’rsatgan holda kerakli argumentlar ham sanab o’tiladi) va kerakli amallar kodi keltiriladi, natijalar umumlashgan holda struktura nomiga o’zlashtiriladi;
3) Asosiy kodda struktura nomi bo’yicha e’lon qilinadi va kerakli funksiyani chaqiruvchi kod kiritiladi.
Tushunarli bo’lishi uchun misolni batafsil izohlari bilan misol keltiramiz:
Yuqoridagi misolda biz int va float turli 2 ta son ustidagi 4 ta amallarni bajaruvchi “hisoblash” nomli funksiyani yaratdik.
Funksiyani tavsiflash va realizatsiya qilish
Yuqorida keltirilgan misollarning barchasida biz funksiyaning qanday e’lon qilinishi va chaqirilishini ko’rib chiqdik. Arduino bibliotekalarini yaratishda va umuman, kodni optimallashtirish maqsadida funksiyaning e’lon qilinishi (tavsiflash) va amalga oshirilishi (realizatsiya) ni ajratib qo’yish tavsiya etiladi. Ya’ni, loyiha boshida uni tavsiflanadi, boshqa joyida (xususan, unga ilova qilingan alohida faylda ham bo’lishi mumkin) realizatsiya qilinadi.
Ko’rinib turibdiki, tavsiflash realizatsiyaning boshlang’ich qismidan iborat xolos. Lekin asosiy qoida shundan iboratki, tavsiflash aynan loyiha boshida amalga oshirilishi kerak, shundan keyin realizatsiyani hatto boshqa fayl yoki bibliotekaga ham ko’chirib qo’yish mumkin (boshqa fayldan qo’shib olish uchun uning nomini #include operatori orqali e’lon qilish ham talab etiladi).
Shablonli funksiyalar
Shablonli funksiya – bu har qanday qiymat turi bilan ishlay oladigan funksiya hisoblanadi. Berilgan qiymatdan kelib chiqib kompilyatorning o’zi uning qanday turga mansubligini aniqlaydi va zarurat bo’lsa, bir nechta haddan tashqari yuklangan funksiya yaratib olishi mumkin.
Shablonli funksiya quyidagicha e’lon qilinadi:
template <shablon_parametrlari> funksiya_tavsifi
template <shablon_paramterlari> qiymat_turi (argumentlar) {
funksiya_kodi
}
Misol sifatida har qanday turdagi sonning kvadratini hisoblash funksiyasini yaratamiz: