25 / 68

Closure

Yopilishlar

Go dasturlash tilida closure (yopilish) — bu tashqi funksiya o'z ishini yakunlagan bo'lsa ham, uning ichidagi o'zgaruvchilarga murojaat qilish imkoniyatini saqlab qoluvchi ichki (ichma-ich) funksiyadir.

Closure tushunchasini to'liq anglash uchun dastlab ikkita muhim jihatni ko'rib chiqamiz:

  1. Ichma-ich funksiyalar (Nested Functions)
  2. Funksiyadan funksiya qaytarish

1. Goda Ichma-ich funksiyalar (Nested Functions)

Go tilida bitta funksiya ichida boshqa bir anonim funksiyani yaratish mumkin:

example.go
package main

import "fmt"

// Tashqi funksiya
func greet(name string) {
    // Ichki funksiya
    var displayName = func() {
        fmt.Println("Salom", name)
    }

    // Ichki funksiyani chaqiramiz
    displayName()
}

func main() {
    greet("Ali") // Salom Ali
}

2. Funksiyadan funksiya qaytarish

Funksiya o'zidan anonim funksiyani qaytarishi mumkin:

example.go
package main

import "fmt"

func greet() func() {
    return func() {
        fmt.Println("Salom Ali")
    }
}

func main() {
    // g1 o'zgaruvchisi qaytgan funksiyani qabul qiladi
    g1 := greet()

    // Funksiyani chaqiramiz
    g1()
}

Natija:

kod
Salom Ali

3. Go Closure qanday ishlaydi?

Oddiy holatda funksiya ishlab bo'lgach, uning mahalliy o'zgaruvchilari xotiradan o'chiriladi. Ammo closure yaratilganda, ichki funksiya tashqi funksiyaning o'zgaruvchisini o'z doirasida "yopib" (saqlab) oladi:

example.go
package main

import "fmt"

// Tashqi funksiya
func greet() func() string {
    // Tashqi o'zgaruvchi
    name := "Ali"

    // Ichki anonim funksiyani qaytaramiz
    return func() string {
        name = "Salom " + name
        return name
    }
}

func main() {
    // Tashqi funksiyani chaqiramiz
    message := greet()

    // Ichki funksiyani chaqiramiz
    fmt.Println(message())
}

Natija:

kod
Salom Ali

Bu yerda greet() o'z ishini tugatgan bo'lsa-da, message() ichidagi funksiya name o'zgaruvchisiga kirish huquqini saqlab qoldi.


4. Misol: Toq sonlar ketma-ketligi

Closure orqali funksiya chaqirilganda o'zgaruvchi holatini (state) saqlab borish juda qulay:

example.go
package main

import "fmt"

func calculate() func() int {
    num := 1

    // Har chaqirilganda num qiymatini 2 ga oshirib boradi
    return func() int {
        num = num + 2
        return num
    }
}

func main() {
    odd := calculate()

    fmt.Println(odd()) // 3
    fmt.Println(odd()) // 5
    fmt.Println(odd()) // 7

    // Yangi closure yaratamiz
    odd2 := calculate()
    fmt.Println(odd2()) // 3
}

Natija:

kod
3
5
7
3

5. Ma'lumotlar izolatsiyasi (Data Isolation)

Har safar tashqi funksiya chaqirilganda, u yangi va mustaqil closure nusxasini qaytaradi. Bir nechta closure nusxalari bir-birining qiymatiga mutlaqo ta'sir qilmaydi:

example.go
package main

import "fmt"

func displayNumbers() func() int {
    number := 0

    return func() int {
        number++
        return number
    }
}

func main() {
    // Birinchi closure
    num1 := displayNumbers()
    fmt.Println(num1()) // 1
    fmt.Println(num1()) // 2
    fmt.Println(num1()) // 3

    // Ikkinchi mustaqil closure
    num2 := displayNumbers()
    fmt.Println(num2()) // 1
    fmt.Println(num2()) // 2
}

Natija:

kod
1
2
3
1
2

num1 chaqirilganda number o'zgaruvchisi 3 ga yetdi, lekin yangi yaratilgan num2 mutlaqo yangi xotira bilan 1 dan boshlandi. Bu dasturlashda ma'lumotlarni xavfsiz va alohida (izolyatsiyalangan) saqlashda juda qo'l keladi.