Memahami Iterator dalam Go: A Fun Dive!

Patricia Arquette
Lepaskan: 2024-10-25 02:28:02
asal
201 orang telah melayarinya

Understanding Iterators in Go: A Fun Dive!

Jika anda seorang pengaturcara Go, anda mungkin pernah mendengar tentang iterator berkali-kali dalam Go 1.22, dan terutamanya dalam Go 1.23 . Tetapi mungkin anda masih menggaru kepala anda, tertanya-tanya mengapa ia berguna atau bila anda perlu menggunakannya. Nah, anda berada di tempat yang betul! Mari mulakan dengan melihat cara iterator berfungsi dalam Go dan sebab ia boleh menjadi sangat berguna.

Satu Transformasi Mudah: Belum Ada Iterator

Bayangkan kita mempunyai senarai nombor, dan kita mahu menggandakan setiap nombor. Kita boleh melakukan ini menggunakan fungsi mudah seperti di bawah:

package main

import (
    "fmt"
)

func NormalTransform[T1, T2 any](list []T1, transform func(T1) T2) []T2 {
    transformed := make([]T2, len(list))

    for i, t := range list {
        transformed[i] = transform(t)
    }

    return transformed
}

func main() {
    list := []int{1, 2, 3, 4, 5}
    doubleFunc := func(i int) int { return i * 2 }

    for i, num := range NormalTransform(list, doubleFunc) {
        fmt.Println(i, num)
    }
}
Salin selepas log masuk
Salin selepas log masuk

Inilah yang berlaku apabila anda menjalankan kod ini:

0 2
1 4
2 6
3 8
4 10
Salin selepas log masuk
Salin selepas log masuk

Agak mudah, bukan? Ini ialah fungsi Go generik asas yang mengambil senarai mana-mana jenis T1, menggunakan fungsi transformasi pada setiap elemen dan mengembalikan senarai baharu dengan senarai diubah bagi sebarang jenis T2. Mudah difahami jika anda tahu Go generik!

Tetapi bagaimana jika saya memberitahu anda ada cara lain untuk menangani perkara ini—menggunakan lelaran?

Masukkan Iterator!

Sekarang, mari kita lihat bagaimana anda boleh menggunakan lelaran untuk transformasi yang sama:

package main

import (
    "fmt"
)

func IteratorTransform[T1, T2 any](list []T1, transform func(T1) T2) iter.Seq2[int, T2] {
    return func(yield func(int, T2) bool) {
        for i, t := range list {
            if !yield(i, transform(t)) {
                return
            }
        }
    }
}

func main() {
    list := []int{1, 2, 3, 4, 5}
    doubleFunc := func(i int) int { return i * 2 }

    for i, num := range NormalTransform(list, doubleFunc) {
        fmt.Println(i, num)
    }
}
Salin selepas log masuk

Sebelum menjalankannya, anda mesti memastikan versi Go anda 1.23. Outputnya betul-betul sama:

0 2
1 4
2 6
3 8
4 10
Salin selepas log masuk
Salin selepas log masuk

Tetapi tunggu, mengapa kita memerlukan iterator di sini? Bukankah itu lebih rumit? Mari kita gali perbezaannya.

Mengapa Menggunakan Iterator?

Pada pandangan pertama, iterator kelihatan agak terlalu kejuruteraan untuk sesuatu yang mudah seperti mengubah senarai. Tetapi apabila anda menjalankan penanda aras, anda mula melihat sebab ia patut dipertimbangkan!

Mari kita tanda aras kedua-dua kaedah dan lihat prestasinya:

package main

import (
    "testing"
)

var (
    transform = func(i int) int { return i * 2 }
    list      = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
)

func BenchmarkNormalTransform(b *testing.B) {
    for i := 0; i < b.N; i++ {
        NormalTransform(list, transform)
    }
}

func BenchmarkIteratorTransform(b *testing.B) {
    for i := 0; i < b.N; i++ {
        IteratorTransform(list, transform)
    }
}
Salin selepas log masuk

Berikut ialah hasil penanda aras awal:

BenchmarkNormalTransform-8      41292933                29.49 ns/op
BenchmarkIteratorTransform-8    1000000000               0.3135 ns/op
Salin selepas log masuk

Wah! Itulah perbezaan yang besar! Tetapi tunggu-ada sedikit ketidakadilan di sini. Fungsi NormalTransform mengembalikan senarai yang diubah sepenuhnya, manakala fungsi IteratorTransform hanya menyediakan iterator tanpa mengubah senarai lagi.

Mari jadikan ia adil dengan menggelung sepenuhnya melalui lelaran:

func BenchmarkIteratorTransform(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for range IteratorTransform(list, transform) {
        }
    }
}
Salin selepas log masuk

Kini hasilnya lebih munasabah:

BenchmarkNormalTransform-8      40758822                29.16 ns/op
BenchmarkIteratorTransform-8    53967146                22.39 ns/op
Salin selepas log masuk

Baiklah, lelarannya lebih pantas. kenapa? Kerana NormalTransform mencipta keseluruhan senarai yang diubah dalam ingatan (pada timbunan) sebelum mengembalikannya, manakala lelaran melakukan transformasi semasa anda mengulanginya, menjimatkan masa dan ingatan.

Baca lebih lanjut tentang Timbunan dan Timbunan di sini

Keajaiban sebenar pengulang berlaku apabila anda tidak perlu memproses keseluruhan senarai. Mari kita tanda aras senario di mana kita hanya mahu mencari nombor 4 selepas menukar senarai:

func BenchmarkNormalTransform(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for _, num := range NormalTransform(list, transform) {
            if num == 4 {
                break
            }
        }
    }
}

func BenchmarkIteratorTransform(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for _, num := range IteratorTransform(list, transform) {
            if num == 4 {
                break
            }
        }
    }
}
Salin selepas log masuk

Hasilnya bercakap untuk diri mereka sendiri:

package main

import (
    "fmt"
)

func NormalTransform[T1, T2 any](list []T1, transform func(T1) T2) []T2 {
    transformed := make([]T2, len(list))

    for i, t := range list {
        transformed[i] = transform(t)
    }

    return transformed
}

func main() {
    list := []int{1, 2, 3, 4, 5}
    doubleFunc := func(i int) int { return i * 2 }

    for i, num := range NormalTransform(list, doubleFunc) {
        fmt.Println(i, num)
    }
}
Salin selepas log masuk
Salin selepas log masuk

Dalam kes ini, iterator adalah lebih pantas! kenapa? Kerana iterator tidak mengubah keseluruhan senarai—ia berhenti sebaik sahaja ia menemui hasil yang anda cari. Sebaliknya, NormalTransform masih mengubah keseluruhan senarai, walaupun kami hanya mengambil berat tentang satu item.

Kesimpulan: Bilakah Menggunakan Iterator?

Jadi, mengapa menggunakan iterator dalam Go?

  • Kecekapan: Iterator boleh menjimatkan masa dan memori dengan tidak memproses keseluruhan senarai jika anda tidak memerlukannya.
  • Fleksibiliti: Ia membolehkan anda mengendalikan set data yang besar dengan cekap, terutamanya apabila bekerja dengan aliran data atau apabila anda perlu berhenti awal. Tetapi perlu diingat, iterator boleh menjadi lebih rumit untuk difahami dan dilaksanakan. Gunakannya apabila anda memerlukan peningkatan prestasi tambahan itu, terutamanya dalam senario di mana anda tidak perlu bekerja dengan keseluruhan senarai di muka.

Pelajar: Mereka pantas, fleksibel dan menyeronokkan—sebaik sahaja anda memahaminya!

Atas ialah kandungan terperinci Memahami Iterator dalam Go: A Fun Dive!. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
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
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!