首頁  >  文章  >  後端開發  >  了解 Go 中的迭代器:有趣的探索!

了解 Go 中的迭代器:有趣的探索!

Patricia Arquette
Patricia Arquette原創
2024-10-25 02:28:02184瀏覽

Understanding Iterators in Go: A Fun Dive!

如果您是Go 程式設計師,您可能在Go 1.22 中多次聽說過迭代器,尤其是在Go 1.23 中。但也許您仍然摸不著頭腦,想知道為什麼它們有用或何時應該使用它們。嗯,您來對地方了!讓我們先了解迭代器在 Go 中的工作原理以及為何它們如此有用。

一個簡單的轉換:還沒有迭代器

假設我們有一個數字列表,我們希望將每個數字加倍。我們可以使用如下所示的簡單函數來完成此操作:

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)
    }
}

執行此程式碼時會發生以下情況:

0 2
1 4
2 6
3 8
4 10

很簡單,對吧?這是一個基本的通用 Go 函數,它接受任何類型 T1 的列表,對每個元素套用轉換函數,並傳回一個新列表,其中包含任何類型 T2 的轉換後的列表。如果您了解 Go 泛型

,那麼很容易理解

但是如果我告訴你還有另一種方法來處理這個問題-使用迭代器呢?

輸入迭代器!

現在,讓我們看看如何使用迭代器進行相同的轉換:

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)
    }
}

在運行之前,您必須確保您的 Go 版本是 1.23。輸出完全相同:

0 2
1 4
2 6
3 8
4 10

但是等等,為什麼我們需要一個迭代器?那不是更複雜嗎?讓我們深入探討一下差異。

為什麼要使用迭代器?

乍一看,對於像轉換清單這樣簡單的事情,迭代器似乎有點過度設計。但當您執行基準測試時,您就會開始明白為什麼它們值得考慮!

讓我們對這兩種方法進行基準測試,看看它們的表現如何:

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)
    }
}

這是初步基準測試結果:

BenchmarkNormalTransform-8      41292933                29.49 ns/op
BenchmarkIteratorTransform-8    1000000000               0.3135 ns/op

哇!這是一個巨大的差異!但是等等——這裡有一點不公平。 NormalTransform 函數傳回一個完全轉換的列表,而 IteratorTransform 函數僅設定迭代器,尚未轉換列表。

讓我們透過完全循環迭代器來使其公平:

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

現在結果更合理了:

BenchmarkNormalTransform-8      40758822                29.16 ns/op
BenchmarkIteratorTransform-8    53967146                22.39 ns/op

好吧,迭代器有點快。為什麼?因為 NormalTransform 在返回之前會在記憶體中(在堆上)創建一個完整的轉換列表,而迭代器會在循環遍歷它時進行轉換,從而節省時間和記憶體。

在此閱讀更多關於堆疊和堆疊的更多資訊

當你不需要處理整個清單時,迭代器的真正魔力就會發生。讓我們對一個場景進行基準測試,在轉換清單後我們只想找到數字 4:

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
            }
        }
    }
}

結果不言而喻:

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)
    }
}

在這種情況下,迭代器快得多!為什麼?因為迭代器不會轉換整個清單 - 一旦找到您要尋找的結果,它就會停止。另一方面,即使我們只關心一項,NormalTransform 仍然會轉換整個清單。

結論:何時使用迭代器?

那麼,為什麼在 Go 中使用迭代器?

  • 效率:如果不需要,迭代器可以不處理整個列表,從而節省時間和記憶體。
  • 靈活性:它們使您能夠有效地處理大型資料集,特別是在處理資料流或需要提前停止時。 但請記住,迭代器的理解和實作可能有點棘手。當您需要額外的效能提升時,尤其是在您不需要預先處理整個清單的情況下,請使用它們。

迭代器:它們快速、靈活且有趣——一旦你掌握了它們!

以上是了解 Go 中的迭代器:有趣的探索!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn