如果您是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中文網其他相關文章!