7-bo‘lim
Funksiyalar
Ko'p qiymat qaytarish, nomlangan natijalar, o'zgaruvchan argumentlar, funksiya qiymat sifatida, yopilmalar va defer.
Ushbu bo‘lim mundarijasi
Go da funksiya - oddiy qurilma, lekin bir nechta muhim xususiyati bor. Eng kattasi: u bir nechta qiymat qaytara oladi.
Oddiy funksiya #
package main
import "fmt"
func qoshish(a int, b int) int {
return a + b
}
func kopaytirish(a, b int) int {
return a * b
}
func salomlash(ism string) {
fmt.Println("Salom,", ism)
}
func main() {
fmt.Println(qoshish(3, 4))
fmt.Println(kopaytirish(3, 4))
salomlash("Malika")
}
7
12
Salom, Malika
Tur nomdan keyin yoziladi: a int, int emas a.
Ketma-ket parametrlarning turi bir xil bo'lsa, uni bir marta yozish
kifoya: func kopaytirish(a, b int).
Ikki qiymat qaytarish #
package main
import "fmt"
func bol(a, b int) (int, error) {
if b == 0 {
return 0, fmt.Errorf("nolga bo'lib bo'lmaydi")
}
return a / b, nil
}
func main() {
natija, xato := bol(10, 2)
fmt.Println(natija, xato)
natija2, xato2 := bol(10, 0)
fmt.Println(natija2, xato2)
if _, xato := bol(5, 0); xato != nil {
fmt.Println("Ushlandi:", xato)
}
}
5 <nil>
0 nolga bo'lib bo'lmaydi
Ushlandi: nolga bo'lib bo'lmaydi
Bu Go dagi eng muhim naqsh. Funksiya natija bilan birga xatoni ham qaytaradi, chaqiruvchi esa uni darhol tekshiradi.
Java yoki Python da xato try/catch bilan ushlanadi. Muammo shundaki,
funksiyaga qarab uning xato tashlashi yoki tashlamasligini bilib
bo'lmaydi - bu ko'rinmaydi.
Go boshqa yo'lni tanladi: xato - oddiy qaytariladigan qiymat. Funksiya imzosiga qarab uning xato berishi mumkinligi darhol ko'rinadi.
Buning narxi - ko'proq yozuv. Foydasi - hech bir xato tasodifan e'tibordan chetda qolmaydi. Buni 14-bo'limda to'liq ko'ramiz.
Nomlangan natijalar #
package main
import "fmt"
func ajrat(sonlar []int) (juftlar, toqlar int) {
for _, s := range sonlar {
if s%2 == 0 {
juftlar++
} else {
toqlar++
}
}
return
}
func main() {
j, t := ajrat([]int{1, 2, 3, 4, 5, 6, 7})
fmt.Println("Juft:", j, "Toq:", t)
}
Juft: 3 Toq: 4
Natijalarga nom berilsa, ular funksiya boshida nol qiymat bilan
e'lon qilinadi. Oxiridagi bo'sh return ularning joriy qiymatini
qaytaradi.
Nomlangan natijalar hujjat sifatida foydali: (juftlar, toqlar int)
imzoning o'zi nima qaytishini aytib turadi.
Lekin bo'sh return (uni "yalang'och return" deyishadi) uzun
funksiyalarda o'qishni qiyinlashtiradi: qaysi qiymat qaytayotganini
ko'rish uchun butun funksiyani o'qish kerak bo'ladi.
Qoida: nom bering, lekin return juftlar, toqlar deb ochiq yozing.
Bo'sh return ni faqat juda qisqa funksiyalarda ishlating.
O'zgaruvchan sondagi argumentlar #
package main
import "fmt"
func yigindi(sonlar ...int) int {
jami := 0
for _, s := range sonlar {
jami += s
}
return jami
}
func main() {
fmt.Println(yigindi(1, 2, 3))
fmt.Println(yigindi())
royxat := []int{10, 20, 30, 40}
fmt.Println(yigindi(royxat...))
}
6
0
100
...int - "istalgancha int" degani. Funksiya ichida sonlar
oddiy []int bo'ladi.
Tayyor slice berish kerak bo'lsa, uning oxiriga ... qo'yiladi:
yigindi(royxat...).
Funksiya - qiymat #
package main
import "fmt"
func qolla(sonlar []int, amal func(int) int) []int {
natija := make([]int, 0, len(sonlar))
for _, s := range sonlar {
natija = append(natija, amal(s))
}
return natija
}
func main() {
sonlar := []int{1, 2, 3, 4}
kvadrat := func(x int) int { return x * x }
fmt.Println(qolla(sonlar, kvadrat))
fmt.Println(qolla(sonlar, func(x int) int { return x + 10 }))
}
[1 4 9 16]
[11 12 13 14]
Funksiyani o'zgaruvchiga berish, argument sifatida uzatish yoki qaytarish mumkin.
Nomsiz funksiya func(x int) int { ... } shaklida yoziladi - uni
anonim funksiya deb atashadi.
Yopilmalar #
package main
import "fmt"
func hisoblagich() func() int {
son := 0
return func() int {
son++
return son
}
}
func main() {
birinchi := hisoblagich()
fmt.Println(birinchi(), birinchi(), birinchi())
ikkinchi := hisoblagich()
fmt.Println(ikkinchi())
}
1 2 3
1
hisoblagich ichidagi son funksiya tugagandan keyin ham
yashashda davom etadi, chunki qaytarilgan funksiya unga bog'lanib
qolgan. Bunday funksiyani yopilma (closure) deb atashadi.
Har bir chaqiruv o'z son ini oladi - shuning uchun ikkinchi
yana 1 dan boshladi.
Sikl o'zgaruvchisi va yopilma #
package main
import "fmt"
func main() {
var funksiyalar []func() int
for i := 1; i <= 3; i++ {
funksiyalar = append(funksiyalar, func() int { return i })
}
for _, f := range funksiyalar {
fmt.Println(f())
}
}
1
2
3
Go 1.21 va undan oldin yuqoridagi kod 4 4 4 chiqarardi.
Sababi: sikl o'zgaruvchisi i butun sikl uchun bitta edi. Uchala
yopilma ham o'sha bitta o'zgaruvchiga bog'langan, sikl tugaganda esa
uning qiymati 4 bo'lib qolgan.
Bu Go tarixidagi eng ko'p uchragan xato edi - ayniqsa gorutinalar
bilan birga (18-bo'lim). Odamlar i := i deb ichkarida nusxa
olishga majbur bo'lishardi.
Go 1.22 buni tuzatdi: endi har bir aylanish o'z i sini
oladi. Yuqoridagi natija - yangi xatti-harakat.
Eski kodni o'qiyotganda i := i qatorini ko'rsangiz, ajablanmang -
bu o'sha davrdan qolgan.
defer #
package main
import "fmt"
func main() {
fmt.Println("boshlandi")
defer fmt.Println("uchinchi")
defer fmt.Println("ikkinchi")
defer fmt.Println("birinchi")
fmt.Println("tugadi")
}
boshlandi
tugadi
birinchi
ikkinchi
uchinchi
defer - "bu buyruqni funksiya tugaganda bajar" degani.
Bir nechta defer bo'lsa, ular teskari tartibda ishlaydi: oxirgi
yozilgani birinchi bajariladi.
Uning asosiy vazifasi - ochilgan narsani yopish. Fayl, tarmoq ulanishi, qulf.
fayl, xato := os.Open("hisobot.txt")
if xato != nil {
return xato
}
defer fayl.Close()
defer ni ochishdan darhol keyin yozasiz. Shundan keyin
funksiyada nechta return bo'lishidan qat'i nazar - hatto vahima
(panic) bo'lsa ham - fayl yopiladi.
Buni 15- va 18-bo'limlarda yana ko'ramiz.
defer argumentni darhol hisoblaydi #
package main
import "fmt"
func main() {
son := 1
defer fmt.Println("defer ko'rgan qiymat:", son)
son = 99
fmt.Println("oxirgi qiymat:", son)
}
oxirgi qiymat: 99
defer ko'rgan qiymat: 1
defer chaqiruvni kechiktiradi, lekin argumentlarni darhol
hisoblaydi.
Yuqorida son defer yozilgan paytda 1 edi - shu qiymat saqlanib
qoldi, garchi chiqarish keyinroq bo'lsa ham.
Oxirgi qiymat kerak bo'lsa, uni yopilma ichiga oling:
defer func() { fmt.Println(son) }()
Endi son chaqiruv paytida o'qiladi va 99 chiqadi.
Rekursiya #
package main
import "fmt"
func faktorial(n int) int {
if n <= 1 {
return 1
}
return n * faktorial(n-1)
}
func main() {
natijalar := []int{}
for i := 1; i <= 8; i++ {
natijalar = append(natijalar, faktorial(i))
}
fmt.Println(natijalar)
}
[1 2 6 24 120 720 5040 40320]
Rekursiv funksiyada har doim to'xtash sharti birinchi turishi kerak. Aks holda dastur to'xtamaydi va xotira tugaydi.
- Ikki sonni qo'shadigan funksiya yozing.
- Natija va xatoni birga qaytaradigan bo'luvchi yozing.
- Nomlangan natijalar bilan juft va toqlarni sanang.
...intbilan istalgancha son qabul qiling.- Tayyor slice ni
...bilan uzating. - Funksiyani argument sifatida qabul qiluvchi yozing.
- Yopilma bilan hisoblagich yasang va ikki marta chaqiring.
- Uchta
deferyozib, tartibini kuzating. deferargumenti darhol hisoblanishini isbotlang.- Rekursiya bilan Fibonachchi sonlarini chiqaring.
Xulosa #
- Tur nomdan keyin yoziladi; bir xil turdagilarni bir marta yozish kifoya.
- Funksiya bir nechta qiymat qaytara oladi;
(natija, error)- Go ning asosiy naqshi. - Go da istisno yo'q: xato oddiy qaytariladigan qiymat.
- Nomlangan natijalar hujjat sifatida foydali, lekin bo'sh
returnni kam ishlating. ...int- o'zgaruvchan argumentlar; tayyor sliceroyxat...bilan uzatiladi.- Funksiya - oddiy qiymat: uzatish, saqlash va qaytarish mumkin.
- Yopilma o'zi tug'ilgan muhitni eslab qoladi.
- Go 1.22 dan beri har bir sikl aylanishi o'z o'zgaruvchisini oladi.
deferfunksiya oxirida ishlaydi, teskari tartibda, lekin argumentlarini darhol hisoblaydi.
Keyingi bo'limda ko'rsatkichlarni o'rganamiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.