7-bo‘lim

Funksiyalar

Ko'p qiymat qaytarish, nomlangan natijalar, o'zgaruvchan argumentlar, funksiya qiymat sifatida, yopilmalar va defer.

🕑 14 daqiqa o‘qish 📄 727 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Oddiy funksiya
  2. Ikki qiymat qaytarish
  3. Nomlangan natijalar
  4. O'zgaruvchan sondagi argumentlar
  5. Funksiya - qiymat
  6. Yopilmalar
  7. Sikl o'zgaruvchisi va yopilma
  8. defer
  9. defer argumentni darhol hisoblaydi
  10. Rekursiya
  11. Xulosa

Go da funksiya - oddiy qurilma, lekin bir nechta muhim xususiyati bor. Eng kattasi: u bir nechta qiymat qaytara oladi.

Oddiy funksiya #

Go
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")
}
Natija
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 #

Go
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)
    }
}
Natija
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.

Nega Go da istisnolar yo'q

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 #

Go
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)
}
Natija
Juft: 3 Toq: 4

Natijalarga nom berilsa, ular funksiya boshida nol qiymat bilan e'lon qilinadi. Oxiridagi bo'sh return ularning joriy qiymatini qaytaradi.

Bo'sh return ni kam ishlating

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 #

Go
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...))
}
Natija
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 #

Go
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 }))
}
Natija
[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 #

Go
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())
}
Natija
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 #

Go
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())
    }
}
Natija
1
2
3
Bu misol eski Go da boshqacha ishlagan

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 #

Go
package main

import "fmt"

func main() {
    fmt.Println("boshlandi")

    defer fmt.Println("uchinchi")
    defer fmt.Println("ikkinchi")
    defer fmt.Println("birinchi")

    fmt.Println("tugadi")
}
Natija
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.

defer nima uchun kerak

Uning asosiy vazifasi - ochilgan narsani yopish. Fayl, tarmoq ulanishi, qulf.

Go
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 #

Go
package main

import "fmt"

func main() {
    son := 1

    defer fmt.Println("defer ko'rgan qiymat:", son)

    son = 99
    fmt.Println("oxirgi qiymat:", son)
}
Natija
oxirgi qiymat: 99
defer ko'rgan qiymat: 1
defer argumenti o'sha zahoti hisoblanadi

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:

Go
defer func() { fmt.Println(son) }()

Endi son chaqiruv paytida o'qiladi va 99 chiqadi.

Rekursiya #

Go
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)
}
Natija
[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.

Amaliy topshiriq
  1. Ikki sonni qo'shadigan funksiya yozing.
  2. Natija va xatoni birga qaytaradigan bo'luvchi yozing.
  3. Nomlangan natijalar bilan juft va toqlarni sanang.
  4. ...int bilan istalgancha son qabul qiling.
  5. Tayyor slice ni ... bilan uzating.
  6. Funksiyani argument sifatida qabul qiluvchi yozing.
  7. Yopilma bilan hisoblagich yasang va ikki marta chaqiring.
  8. Uchta defer yozib, tartibini kuzating.
  9. defer argumenti darhol hisoblanishini isbotlang.
  10. 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 return ni kam ishlating.
  • ...int - o'zgaruvchan argumentlar; tayyor slice royxat... 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.
  • defer funksiya oxirida ishlaydi, teskari tartibda, lekin argumentlarini darhol hisoblaydi.

Keyingi bo'limda ko'rsatkichlarni o'rganamiz.

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.