Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Memahami terator Go

Memahami terator Go

王林
王林asal
2024-08-18 06:32:06972semak imbas

Understanding Go terators

Ramai orang nampaknya keliru dengan iterator yang baru ditambah dalam Go, itulah sebabnya saya memutuskan untuk menulis satu lagi artikel cuba menerangkannya dengan cara yang semudah mungkin.

Bagaimana mereka dipanggil oleh Go?

Pertama, saya fikir adalah penting untuk memahami cara iterator dipanggil dan digunakan oleh Go, dan ia sebenarnya agak mudah, mari kita gunakan kepingan.Semua iterator sebagai contoh. Berikut ialah cara anda biasanya menggunakan iterator ini:

package main

import (
    "fmt"
    "slices"
)

func main() {
    slice := []string{
        "Element 1",
        "Element 2",
        "Element 3",
        "Element 4",
    }

    for index, element := range slices.All(slice) {
        if index >= 2 {
            break
        }
        fmt.Println(index, element)
    }

    // Output:
    // 0 Element 1
    // 1 Element 2
}

Dan inilah rupa sebenarnya:

package main

import (
    "fmt"
    "slices"
)

func main() {
    slice := []string{
        "Element 1",
        "Element 2",
        "Element 3",
        "Element 4",
    }

    slices.All(slice)(func (index int, element string) bool {
        if index >= 2 {
            return false // break
        }
        fmt.Println(index, element)

        return true // continue loop as normal
    })

    // Output:
    // 0 Element 1
    // 1 Element 2
}

Apa yang berlaku ialah badan gelung sedang "digerakkan" untuk menghasilkan fungsi yang dihantar kepada lelaran, manakala continue dan break masing-masing diubah untuk mengembalikan true dan return false. return true juga ditambahkan pada penghujung gelung untuk menandakan bahawa kami ingin mendapatkan elemen seterusnya, jika tiada keputusan lain yang dibuat sebelum ini.

Ini bukan pendedahan yang tepat tentang apa yang dilakukan pengkompil dan saya belum menyemak pelaksanaan Go untuk menyemak ini, tetapi mereka menghasilkan hasil yang setara daripada pemerhatian saya.

Bagaimana untuk mencipta iterator anda sendiri dan pelaksanaannya

Sekarang, setelah anda memahami cara mereka dipanggil dan menyedari betapa mudahnya ia sebenarnya, lebih mudah untuk memahami cara mencipta iterator anda sendiri dan pelaksanaannya.

Mari kita buat iterator nyahpepijat yang akan mencetak mesej nyahpepijat untuk setiap langkah pelaksanaan iterator yang akan meninjau semua elemen dalam kepingan (slices.Semua fungsi).

Pertama, saya akan mencipta fungsi pembantu kecil untuk log keluar mesej dengan masa pelaksanaan semasa.

import (
    "fmt"
    "time"
)

var START time.Time = time.Now()

func logt(message string) {
    fmt.Println(time.Since(START), message)
}

Kembali kepada iterator:

import (
    "iter"
)

func DebugIter[E any](slice []E) iter.Seq2[int, E] {
    logt("DebugIter called")

    // the same way iter.All returned function
    // we called in order to iterate over slice
    // here we are returning a function to
    // iterate over all slice elements too
    return func(yield func(int, E) bool) {
        logt("Seq2 return function called, starting loop")
        for index, element := range slice {
            logt("in loop, calling yield")
            shouldContinue := yield(index, element)
            if !shouldContinue {
                logt("in loop, yield returned false")
                return
            }
            logt("in loop, yield returned true")
        }
    }
}

Saya telah menambah beberapa kenyataan cetakan nyahpepijat supaya kita dapat melihat dengan lebih baik susunan pelaksanaan iterator dan cara ia bertindak balas terhadap kata kunci yang berbeza seperti putus dan teruskan.

Akhir sekali, mari gunakan iterator yang dilaksanakan:

func main() {
    slice := []string{
        "Element 1",
        "Element 2",
        "Element 3",
        "Element 4",
    }

    for index, element := range DebugIter(slice) {
        message := "got element in range of iter: " + element
        logt(message)
        if index >= 2 {
            break
        }
        if index > 0 {
            continue
        }
        time.Sleep(2 * time.Second)
        logt("ended sleep in range of iter")
    }
}

Akan memberikan kami output:

11.125µs DebugIter called
39.292µs Seq2 return function called, starting loop
42.459µs in loop, calling yield
44.292µs got element in range of iter: Element 1
2.001194292s ended sleep in range of iter
2.001280459s in loop, yield returned true
2.001283917s in loop, calling yield
2.001287042s got element in range of iter: Element 2
2.001291084s in loop, yield returned true
2.001293125s in loop, calling yield
2.0012955s got element in range of iter: Element 3
2.001297542s in loop, yield returned false

Contoh ini menunjukkan dengan baik cara iterator berfungsi dan dilaksanakan. Apabila menggunakan iterator dalam gelung julat semua arahan dalam blok gelung adalah jenis "dipindahkan" ke fungsi yang dipanggil hasil. Apabila kita memanggil hasil, kita pada asasnya meminta go runtime untuk melaksanakan apa sahaja yang terdapat dalam blok gelung dengan nilai berikut untuk lelaran ini, itulah juga sebabnya hasil akan disekat, jika badan gelung akan disekat. Sekiranya masa jalanan menentukan bahawa lelaran gelung ini sepatutnya berhenti, hasil akan kembali palsu, ia boleh berlaku apabila kata kunci pemecahan dipenuhi semasa pelaksanaan blok gelung, kita tidak seharusnya memanggil hasil lagi jika itu berlaku. Jika tidak, kita harus terus memanggil hasil.

Kod penuh:

package main

import (
    "fmt"
    "time"
    "iter"
)

var START time.Time = time.Now()

func logt(message string) {
    fmt.Println(time.Since(START), message)
}

func DebugIter[E any](slice []E) iter.Seq2[int, E] {
    logt("DebugIter called")

    // the same way iter.All returned function
    // we called in order to iterate over slice
    // here we are returning a function to
    // iterate over all slice elements too
    return func(yield func(int, E) bool) {
        logt("Seq2 return function called, starting loop")
        for index, element := range slice {
            logt("in loop, calling yield for")
            shouldContinue := yield(index, element)
            if !shouldContinue {
                logt("in loop, yield returned false")
                return
            }
            logt("in loop, yield returned true")
        }
    }
}

func main() {
    slice := []string{
        "Element 1",
        "Element 2",
        "Element 3",
        "Element 4",
    }

    for index, element := range DebugIter(slice) {
        message := "got element in range of iter: " + element
        logt(message)
        if index >= 2 {
            break
        }
        if index > 0 {
            continue
        }
        time.Sleep(2 * time.Second)
        logt("ended sleep in range of iter")
    }

    // unfold compiler magic
    //  DebugIter(slice)(func (index int, element string) bool {
    //    message := "got element in range of iter: " + element
    //    logt(message)
    //    if index >= 2 {
    //      return false
    //    }
    //    if index > 0 {
    //      return true
    //    }
    //    time.Sleep(2 * time.Second)
    //    logt("ended sleep in range of iter")
    //
    //    return true
    //  })
}

Atas ialah kandungan terperinci Memahami terator Go. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn