Goda funksiyalar
Funksiya ma'lum vazifani bajaradigan nomlangan kod bloki. Takrorlanadigan kodni bir joyga yig'ish, dasturni kichik qismlarga ajratish va har bir qismni alohida testlash imkonini beradi.
E'lon qilish va chaqirish
Parametr — funksiya e'lonida ko'rsatilgan o'zgaruvchi. Argument esa funksiya chaqirilganda parametrga beriladigan aniq qiymat.
package main
import "fmt"
func salomlash(ism string) string {
return "Salom, " + ism + "!"
}
func main() {
xabar := salomlash("Ali")
fmt.Println(xabar)
}
Natija:
salomlash bitta string parametrni qabul qiladi va string qaytaradi. return funksiya ishini yakunlab, qiymatni
qaytaradi. Hech qanday qiymat qaytarmaydigan funksiyaga e'lon qilishda qaytish turi yozilmaydi.
Bir xil turdagi ketma-ket parametrlar qisqa yozilishi mumkin: func qo'sh(a, b int) int.
Bir nechta qiymat qaytarish
Go funksiyasi bir nechta qiymat qaytara oladi. Bu imkoniyat natija bilan birga xatoni ham qaytarishda ko'p ishlatiladi:
package main
import "fmt"
func bolish(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("nolga bo'lish mumkin emas")
}
return a / b, nil
}
func main() {
natija, err := bolish(10, 4)
if err != nil {
fmt.Println("Xato:", err)
return
}
fmt.Println("Natija:", natija)
}
Natija:
b nol bo'lsa, bo'lish bajarilmaydi va xato qaytariladi. API, fayl yoki ma'lumotlar bazasi bilan ishlaydigan kodda
xatoni alohida qiymat sifatida qaytarish ko'p qo'llanadi. Qaytgan qiymatlardan biri kerak bo'lmasa, uni _ yordamida
e'tiborsiz qoldirish mumkin.
Argumentlar qiymat sifatida uzatiladi
Goda barcha argumentlar qiymat bo'yicha uzatiladi. Funksiya argument qiymatining nusxasini oladi:
package main
import "fmt"
func oshir(n int) {
n++
}
func main() {
son := 10
oshir(son)
fmt.Println(son)
}
Natija:
slice, map, channel va pointer ham qiymat bo'yicha uzatiladi. Ammo ularning nusxasi ham o'sha asosiy ma'lumotga
murojaat qilishi mumkin. Shu sabab argument nusxalanishi funksiya ichidagi o'zgarishlar chaqiruvchiga hech qachon
ta'sir qilmaydi degani emas.
Variadik funksiya
Ba'zan funksiyaga nechta argument berilishini oldindan aniq bilmaymiz.
Masalan, bir nechta sonning yig'indisini hisoblaydigan funksiya yozmoqchimiz. Oddiy parametrlar bilan yozsak, argumentlar sonini oldindan belgilashimiz kerak:
Bu funksiya faqat ikkita son qabul qiladi:
Agar uchta, to'rtta yoki undan ko'p son uzatmoqchi bo'lsak, bunday funksiya yetarli bo'lmaydi. Shunday holatlarda variadik funksiya ishlatiladi. Variadik parametr quyidagi ko'rinishda yoziladi:
Bu yozuv:
sonlarparametriga nol yoki undan ko'pintqiymat uzatish mumkin
degan ma'noni anglatadi.
Masalan:
package main
import "fmt"
func yigindi(sonlar ...int) int {
natija := 0
for _, son := range sonlar {
natija += son
}
return natija
}
func main() {
fmt.Println(yigindi(1, 2, 3, 4))
}
Natija:
Bu yerda:
funksiyaga to'rtta alohida int argument uzatmoqda.
Funksiya ichida esa:
parametri []int kabi ishlaydi.
Ya'ni yuqoridagi chaqiruvda funksiya ichidagi sonlarni taxminan quyidagicha tasavvur qilish mumkin:
Shuning uchun uning elementlarini range yordamida aylanib chiqish mumkin:
Variadik funksiyaga argumentlar soni turlicha bo'lishi mumkin:
fmt.Println(yigindi())
fmt.Println(yigindi(10))
fmt.Println(yigindi(10, 20))
fmt.Println(yigindi(10, 20, 30, 40))
Masalan:
ham to'g'ri chaqiruv hisoblanadi, chunki variadik parametr nol yoki undan ko'p argument qabul qiladi.
Sliceni variadik funksiyaga uzatish
Endi bizda tayyor slice bor deb tasavvur qilamiz:
Uni quyidagicha yozib bo'lmaydi:
Chunki yigindi alohida int argumentlarni kutmoqda, sonlar esa bitta []int qiymat.
Bunday holatda slice nomidan keyin ... yoziladi:
To'liq misol:
package main
import "fmt"
func yigindi(sonlar ...int) int {
natija := 0
for _, son := range sonlar {
natija += son
}
return natija
}
func main() {
sonlar := []int{5, 6, 7}
fmt.Println(yigindi(sonlar...))
}
Natija:
Quyidagi yozuv:
slice ichidagi elementlarni funksiyaga alohida argumentlar sifatida uzatadi.
Uni mazmunan quyidagiga o'xshatish mumkin:
Demak, ... ikki joyda uchrashi mumkin va ularning vazifasi biroz farq qiladi.
Funksiya parametrida:
...int:
bu parametr istalgan miqdordagi
intargumentlarni qabul qiladi
degan ma'noni bildiradi.
Funksiyani chaqirishda:
sonlar...:
slice ichidagi elementlarni alohida argumentlarga yoyib uzat
degan ma'noni bildiradi.
Variadik parametr oxirgi bo'lishi kerak
Funksiyada oddiy parametrlar ham, variadik parametr ham bo'lishi mumkin:
Bunda funksiya quyidagicha chaqirilishi mumkin:
Ammo variadik parametr har doim parametrlar ro'yxatining oxirida kelishi kerak.
Masalan, quyidagicha yozish mumkin emas:
Sababi, sonlar nechta argument qabul qilishini oldindan bilib bo'lmaydi. Agar undan keyin yana parametr bo'lsa, qaysi
argument variadik parametrga, qaysi biri keyingi parametrga tegishli ekanini aniqlash noaniq bo'lib qoladi.
Shuning uchun to'g'ri ko'rinish:
Qisqacha:
— funksiya istalgan miqdordagi int argument qabul qiladi.
— uchta alohida argument uzatiladi.
— slice elementlari alohida argumentlar sifatida uzatiladi.
Funksiya ichida esa sonlar []int sifatida ishlatiladi.
Funksiya qiymati va anonim funksiya
Goda funksiya ham qiymat sifatida ishlatiladi. Uni o'zgaruvchiga saqlash yoki boshqa funksiyaga argument sifatida uzatish mumkin.
package main
import "fmt"
func hisobla(a, b int, amal func(int, int) int) int {
return amal(a, b)
}
func main() {
kopaytir := func(a, b int) int {
return a * b
}
fmt.Println(hisobla(4, 5, kopaytir))
}
Natija:
Anonim funksiya tashqi ko'lamdagi (scope) o'zgaruvchidan foydalansa, closure hosil bo'ladi. Closure o'sha o'zgaruvchiga murojaatni saqlaydi, shu sabab uning holati keyingi chaqiruvlarda ham saqlanib qolishi mumkin. Bu usul callback, middleware va konfiguratsiya funksiyalarida uchraydi.
Rekursiya
Funksiyaning o'zini chaqirishi rekursiya deyiladi. Rekursiv funksiya albatta to'xtash shartiga ega bo'lishi kerak:
package main
import "fmt"
func faktorial(n uint64) uint64 {
if n <= 1 {
return 1
}
return n * faktorial(n-1)
}
func main() {
fmt.Println(faktorial(5))
}
Natija:
Har bir chaqiruv stackda qo'shimcha joy ishlatadi. Juda chuqur rekursiya resurs sarfini oshiradi. Oddiy ketma-ket
hisoblarda sikldan foydalanish tushunarliroq bo'lishi mumkin. Faktorial natijasi uint64 sig'imidan oshsa, overflow yuz
beradi. Shuning uchun bu misol katta sonlarni hisoblash uchun mo'ljallanmagan.
Misollar
1. Sonning juftligini qaytarish
package main
import "fmt"
func juftmi(son int) bool {
return son%2 == 0
}
func main() {
fmt.Println(juftmi(18))
}
Funksiya hisoblangan bool qiymatni bevosita return orqali qaytaradi. Bu misolning natijasi true bo'ladi.
2. Nomlangan qaytish qiymati
package main
import "fmt"
func tortburchak(eni, boyi int) (yuza int, perimetr int) {
yuza = eni * boyi
perimetr = 2 * (eni + boyi)
return
}
func main() {
yuza, perimetr := tortburchak(8, 5)
fmt.Println(yuza, perimetr)
}
Nomlangan qaytish qiymatlari funksiya ichida oddiy o'zgaruvchi kabi ishlaydi. Qiymatsiz return ularning joriy
qiymatini qaytaradi.
3. Erta return bilan tekshiruv
package main
import "fmt"
func bol(a, b float64) (float64, bool) {
if b == 0 {
return 0, false
}
return a / b, true
}
func main() {
natija, bajarildi := bol(20, 4)
fmt.Println(natija, bajarildi)
}
Nolga bo'lish holati avval tekshiriladi. Ikkinchi qaytish qiymati bo'lish muvaffaqiyatli bajarilganini bildiradi.
4. Variadik yig'indi
package main
import "fmt"
func yigindi(sonlar ...int) int {
natija := 0
for _, son := range sonlar {
natija += son
}
return natija
}
func main() {
fmt.Println(yigindi(2, 4, 6, 8))
}
...int funksiya nol yoki undan ko'p int argument qabul qilishini bildiradi. Funksiya ichida sonlar slice sifatida
ishlaydi.
5. Slice'ni variadik argument sifatida uzatish
package main
import "fmt"
func engKatta(sonlar ...int) int {
katta := sonlar[0]
for _, son := range sonlar[1:] {
if son > katta {
katta = son
}
}
return katta
}
func main() {
sonlar := []int{7, 12, 4, 19}
fmt.Println(engKatta(sonlar...))
}
sonlar... slice elementlarini alohida argumentlar sifatida uzatadi. Misolda slice bo'sh emasligi oldindan ma'lum.
6. Funksiyani argument sifatida uzatish
package main
import "fmt"
func hisobla(a, b int, amal func(int, int) int) int {
return amal(a, b)
}
func main() {
kopaytir := func(a, b int) int { return a * b }
fmt.Println(hisobla(6, 7, kopaytir))
}
amal parametrining turi ikkita int qabul qilib, bitta int qaytaradigan funksiyani bildiradi.
7. Closure ichida holat saqlash
package main
import "fmt"
func hisoblagich() func() int {
son := 0
return func() int {
son++
return son
}
}
func main() {
keyingi := hisoblagich()
fmt.Println(keyingi(), keyingi(), keyingi())
}
Qaytarilgan anonim funksiya tashqi son o'zgaruvchisiga murojaatni saqlaydi. Shu sabab ketma-ket chaqiruvlar natijasi
1 2 3 bo'ladi.
8. Funksiyalar map'i
package main
import "fmt"
func main() {
amallar := map[string]func(int, int) int{
"+": func(a, b int) int { return a + b },
"-": func(a, b int) int { return a - b },
}
fmt.Println(amallar["+"](10, 3))
}
Funksiya ham map qiymati bo'la oladi. Avval funksiya kalit orqali olinadi, keyin argumentlar bilan chaqiriladi.
9. Rekursiya bilan raqamlar yig'indisi
package main
import "fmt"
func raqamlarYigindisi(son int) int {
if son == 0 {
return 0
}
return son%10 + raqamlarYigindisi(son/10)
}
func main() {
fmt.Println(raqamlarYigindisi(5832))
}
Har bir chaqiruv sonning oxirgi raqamini ajratadi. son == 0 sharti rekursiyani to'xtatadi.
10. Funksiyani shartga qarab qaytarish
package main
import "fmt"
func amalniTanla(nom string) func(int, int) int {
if nom == "kopaytir" {
return func(a, b int) int { return a * b }
}
return func(a, b int) int { return a + b }
}
func main() {
amal := amalniTanla("kopaytir")
fmt.Println(amal(5, 4))
}
Funksiya natija sifatida boshqa funksiyani qaytarishi mumkin. Qaytgan qiymat keyin oddiy funksiya kabi chaqiriladi.