Darsliklar Go Lang Closure

Go Lang Go Lang Closure

Closure (yopilgan funksiya) — o’zini o’rab turgan doiradagi o’zgaruvchilarni “eslab qoladigan” anonim funksiya. Go’da funksiyalar birinchi darajali qiymat bo’lgani uchun closure yaratish tabiiy va keng qo’llaniladigan naqsh.

Closuretashqi funksiyaning o’zgaruvchilariga ishora saqlagan holda qaytariladigan anonim funksiya. O’zgaruvchi qiymati emas, aynan o’zi “eslab qolinadi” — shu sabab keyingi o’zgarishlar ham ko’rinadi.

Anonim funksiya

Funksiyani nomsiz, to’g’ridan-to’g’ri o’zgaruvchiga yozish mumkin:

package main

import "fmt"

func main() {
    salomlash := func(ism string) {
        fmt.Println("Salom,", ism)
    }

    salomlash("Farrux")
    salomlash("Master Sherkulov")
}
Salom, Farrux
Salom, Master Sherkulov

func(ism string) { ... } — nomsiz funksiya, salomlash o’zgaruvchisiga saqlandi va oddiy funksiya kabi chaqirildi.

Closure — tashqi o’zgaruvchini “eslab qolish”

Funksiya o’zini o’rab turgan doiradagi o’zgaruvchiga ishora saqlab qolishi mumkin — hatto shu o’zgaruvchini yaratgan funksiya tugagandan keyin ham:

package main

import "fmt"

func hisoblagichYarat() func() int {
    son := 0
    return func() int {
        son++
        return son
    }
}

func main() {
    keyingi := hisoblagichYarat()

    fmt.Println(keyingi())
    fmt.Println(keyingi())
    fmt.Println(keyingi())
}
1
2
3

hisoblagichYarat ishini tugatib, sonni “yo’qotib qo’yishi” kerakdek tuyuladi, lekin qaytarilgan anonim funksiya songa ishorani saqlab qoladi. Har keyingi() chaqiruvi xuddi shu, umumiy sonni oshiradi — bu closure’ning mohiyati.

Har bir chaqiruv — mustaqil closure

Muhim nozik jihat: hisoblagichYarat har chaqirilganda yangi, mustaqil son yaratiladi — closure’lar bir-biriga ta’sir qilmaydi:

package main

import "fmt"

func hisoblagichYarat() func() int {
    son := 0
    return func() int {
        son++
        return son
    }
}

func main() {
    birinchi := hisoblagichYarat()
    ikkinchi := hisoblagichYarat()

    fmt.Println(birinchi())
    fmt.Println(birinchi())
    fmt.Println(ikkinchi())
}
1
2
1

birinchi va ikkinchi — ikkita mustaqil hisoblagichYarat() chaqiruvidan kelib chiqqan, ikkita mustaqil songa ega closure. ikkinchi()ning birinchi chaqiruvi 1dan boshlandi — birinchining holati unga umuman ta’sir qilmadi.

Tsikl ichidagi closure (Go 1.22+)

Tarixan, ko’p tillarda (shu jumladan Go’ning 1.22’gacha bo’lgan versiyalarida) tsikl o’zgaruvchisini closure ichida ishlatish nozik xato manbai bo’lgan — barcha closure bitta, umumiy o’zgaruvchini “ko’rgan”. Go 1.22’dan boshlab bu muammo tilning o’zida tuzatilgan: endi har aylanish o’zining mustaqil nusxasini oladi:

package main

import "fmt"

func main() {
    var funksiyalar []func()

    for i := 0; i < 3; i++ {
        funksiyalar = append(funksiyalar, func() {
            fmt.Println(i)
        })
    }

    for _, f := range funksiyalar {
        f()
    }
}
0
1
2

Go 1.22’dan oldin bu kod 0, 1, 2 o’rniga 3, 3, 3 chiqarardi — chunki barcha uchta closure bitta, sikldan keyin 3ga yetgan iga ishora qilardi. Zamonaviy Go’da har for aylanishi ining yangi nusxasini yaratadi, shuning uchun har bir closure o’zi yaratilgan paytdagi qiymatni to’g’ri “eslab qoladi”.