Tarkibga o'tish

for: kodni takrorlash

Sikl bir xil kodni qayta-qayta yozmasdan takrorlash imkonini beradi. Masalan, 1 dan 100 gacha sonlarni chiqarish, yig‘indini hisoblash yoki matndagi belgilarni navbat bilan ko‘rish uchun sikl kerak.

Go’da sikl uchun faqat for kalit so‘zi bor. U boshqa tillardagi klassik for, while va cheksiz sikl vazifalarini bajaradi.

Uch qismli for

Eng ko‘p uchraydigan shakl uchta qismdan iborat:

for boshlash; shart; yangilash {
    // takrorlanadigan kod
}
  • boshlash sikl boshlanishidan oldin bir marta bajariladi;
  • shart har bir iteratsiyadan oldin tekshiriladi;
  • yangilash har bir iteratsiya oxirida bajariladi.

Iteratsiya — sikl tanasining bir marta bajarilishi.

main.go
1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }
}

Natija:

0
1
2
3
4

Avval i := 0 bir marta bajariladi. i < 5 true ekan, i chop etiladi. So‘ng i++ qiymatni bittaga oshiradi va shart yana tekshiriladi. i 5ga yetganda shart false bo‘ladi va sikl tugaydi.

i faqat for bloki ichida ko‘rinadi. Sikldan keyin fmt.Println(i) yozish kompilyatsiya xatosiga olib keladi.

Info

i++ Go’da faqat alohida buyruq sifatida ishlatiladi. x := i++ yoki if i++ > 2 deb yozib bo‘lmaydi.

Faqat shartli for

Boshlash va yangilash qismlarini tashqariga olib chiqilsa, for boshqa tillardagi while kabi ishlaydi:

main.go
package main

import "fmt"

func main() {
    i := 1
    for i <= 3 {
        fmt.Println(i)
        i++
    }
}

Natija:

1
2
3

Bu shaklda i sikldan oldin yaratilgani uchun undan sikldan keyin ham foydalanish mumkin. Yangilashni unutish xavfli: i o‘zgarmasa, i <= 3 doim true qoladi va sikl tugamaydi.

Cheksiz sikl

Shart ham yozilmasa, sikl cheksiz davom etadi:

for {
    // server so‘rov kutishi yoki navbatdan ish olishi mumkin
}

Cheksiz sikl server, worker va hodisalarni qayta ishlovchi dasturlarda kerak bo‘ladi. Uni terminalni yopish bilan emas, dastur mantiqidagi break, return, signal yoki bekor qilish sharti bilan boshqarish lozim.

main.go
package main

import "fmt"

func main() {
    son := 1

    for {
        if son > 3 {
            break
        }

        fmt.Println(son)
        son++
    }
}

Natija:

1
2
3

son > 3 bo‘lganda break eng yaqin forni darhol tugatadi. breakdan keyingi sikl tanasi bajarilmaydi.

continue: joriy iteratsiyani o‘tkazish

continue joriy iteratsiyaning qolgan qismini tashlab, keyingi iteratsiyaga o‘tadi. Quyidagi dastur faqat juft sonlarni chiqaradi:

main.go
package main

import "fmt"

func main() {
    for i := 1; i <= 10; i++ {
        if i%2 != 0 {
            continue
        }

        fmt.Println(i)
    }
}

Natija:

2
4
6
8
10

i%2 sonni 2ga bo‘lgandagi qoldiqni beradi. Toq sonda qoldiq nol emas va continue sabab Println() bajarilmaydi. Uch qismli forda continuedan keyin yangilash qismi, ya’ni i++ baribir bajariladi.

Faqat juft sonlarni sanash kerak bo‘lsa, qadamni ikkiga oshirish yanada to‘g‘ri:

for i := 2; i <= 10; i += 2 {
    fmt.Println(i)
}

Bu variant toq sonlarni umuman tekshirmaydi. continue esa haqiqiy dasturda bir nechta filtrlash sharti bo‘lganda foydali.

Amaliy misol: kiritilgan sonlar yig‘indisi

Foydalanuvchidan nechta son kiritilishini olib, ularning yig‘indisini hisoblaymiz:

main.go
package main

import "fmt"

func main() {
    var count int

    fmt.Print("Sonlar miqdorini kiriting: ")
    _, err := fmt.Scan(&count)
    if err != nil {
        fmt.Println("Xato: butun son kiriting.")
        return
    }
    if count < 1 || count > 1000 {
        fmt.Println("Xato: miqdor 1 dan 1000 gacha bo‘lishi kerak.")
        return
    }

    sum := 0
    for i := 1; i <= count; i++ {
        var number int

        fmt.Printf("%d-sonni kiriting: ", i)
        _, err = fmt.Scan(&number)
        if err != nil {
            fmt.Println("Xato: barcha qiymatlar butun son bo‘lishi kerak.")
            return
        }

        sum += number
    }

    fmt.Println("Yig‘indi:", sum)
}

3, so‘ng 10, 20 va -5 kiritilsa:

Sonlar miqdorini kiriting: 3
1-sonni kiriting: 10
2-sonni kiriting: 20
3-sonni kiriting: -5
Yig‘indi: 25

count tashqi qiymat bo‘lgani uchun uning turi ham, ruxsat etilgan oralig‘i ham tekshirildi. Sikl aynan count marta ishlaydi. Har bir qiymat sum += number orqali yig‘indiga qo‘shiladi. count uchun yuqori chegara qo‘yish tasodifan juda uzoq sikl ishga tushishining oldini oladi.

Warning

Juda ko‘p yoki juda katta int qiymatlarni qo‘shish overflowga olib kelishi mumkin. Go signed integer overflowini runtime xatosi sifatida to‘xtatmaydi. Moliyaviy yoki ishonchsiz tashqi ma’lumot bilan ishlaganda chegaralarni talabga mos tekshirish zarur.

range bilan qiymatlar bo‘ylab yurish

range string, array, slice, map va channel elementlari bo‘ylab yurish uchun ishlatiladi. Quyidagi string misolida har bir Unicode belgi va uning boshlanish bayt indeksi olinadi:

main.go
package main

import "fmt"

func main() {
    word := "Go‘zal"

    for index, char := range word {
        fmt.Printf("indeks=%d belgi=%c\n", index, char)
    }
}

Natija:

indeks=0 belgi=G
indeks=1 belgi=o
indeks=2 belgi=‘
indeks=5 belgi=z
indeks=6 belgi=a
indeks=7 belgi=l

Indekslar 0, 1, 2, 5... bo‘lishining sababi belgisi UTF-8da uch bayt egallashidir. char turi rune bo‘ladi. String, byte va rune farqi keyingi darsda chuqurroq ko‘riladi.

Qiymatlardan biri kerak bo‘lmasa, bo‘sh identifikator _ ishlatiladi:

for _, char := range word {
    fmt.Printf("%c ", char)
}

Faqat indeks kerak bo‘lsa, ikkinchi qiymatni yozmaslik mumkin:

for index := range word {
    fmt.Println(index)
}

Ichma-ich sikl va belgili break

Bir sikl ichida boshqa sikl yozish mumkin. Oddiy break faqat eng yaqin siklni tugatadi. Tashqi siklni ham tugatish kerak bo‘lsa, belgi (label) ishlatiladi:

main.go
package main

import "fmt"

func main() {
outer:
    for row := 1; row <= 3; row++ {
        for column := 1; column <= 3; column++ {
            if row == 2 && column == 2 {
                break outer
            }
            fmt.Println(row, column)
        }
    }
}

Natija:

1 1
1 2
1 3
2 1

break outer outer: belgisi qo‘yilgan tashqi siklni tugatadi. Belgilar chuqur ichma-ich boshqaruvda foydali, ammo ko‘p ishlatilsa kod oqimini tushunish qiyinlashadi.

Keng tarqalgan xatolar

Chegarani bittaga xato belgilash

i < 10 10ni o‘z ichiga olmaydi, i <= 10 esa oladi. Bu off-by-one xatosi deyiladi. Birinchi, oxirgi va bo‘sh oraliq holatlarini tekshirish muhim.

Hisoblagichni noto‘g‘ri tomonga o‘zgartirish

// Noto‘g‘ri: i kamaygani uchun i < 10 doim true qoladi.
for i := 0; i < 10; i-- {
    fmt.Println(i)
}

Shart, boshlang‘ich qiymat va yangilash bir-biriga mos bo‘lishi kerak. Pastga sanashda, masalan, for i := 10; i >= 1; i-- yoziladi.

Keraksiz cheksiz sikl

Faqat shartli forda shartga ta’sir qiluvchi qiymat yangilanmasa, sikl tugamaydi. Cheksiz sikl ataylab yozilganda ham undan chiqish yoki dasturni xavfsiz to‘xtatish mexanizmi bo‘lishi kerak.

break va continueni adashtirish

break siklni butunlay tugatadi. continue faqat joriy iteratsiyaning qolgan qismini o‘tkazadi. Qaysi biri kerakligini biznes qoidasi belgilaydi.

Xulosa

Go’dagi yagona sikl operatori fordir. U uch qismli, faqat shartli, cheksiz yoki rangeli shaklda yoziladi. break siklni tugatadi, continue keyingi iteratsiyaga o‘tadi. To‘xtash sharti, chegara qiymati va hisoblagich yo‘nalishini aniq belgilash sikl xatolarining oldini oladi.

Keyingi mavzularda range array, slice va map ma’lumotlari bilan ishlashda yana qo‘llanadi.