Wenn Sie ein Go-Programmierer sind, haben Sie wahrscheinlich schon oft von Iteratoren in Go 1.22 und insbesondere in Go 1.23 gehört . Aber vielleicht zweifeln Sie immer noch am Kopf und fragen sich, warum sie nützlich sind oder wann Sie sie verwenden sollten. Dann sind Sie hier genau richtig! Schauen wir uns zunächst an, wie Iteratoren in Go funktionieren und warum sie so nützlich sein können.
Stellen Sie sich vor, wir haben eine Liste mit Zahlen und möchten jede Zahl verdoppeln. Wir könnten dies mit einer einfachen Funktion wie der folgenden tun:
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) } }
Folgendes passiert, wenn Sie diesen Code ausführen:
0 2 1 4 2 6 3 8 4 10
Ganz einfach, oder? Dies ist eine grundlegende generische Go-Funktion, die eine Liste eines beliebigen Typs T1 übernimmt, eine Transformationsfunktion auf jedes Element anwendet und eine neue Liste mit der transformierten Liste eines beliebigen Typs T2 zurückgibt. Leicht zu verstehen, wenn Sie Go Generika!
kennenAber was wäre, wenn ich Ihnen sagen würde, dass es eine andere Möglichkeit gibt, damit umzugehen – die Verwendung eines Iterators?
Lassen Sie uns nun einen Blick darauf werfen, wie Sie einen Iterator für dieselbe Transformation verwenden können:
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) } }
Bevor Sie es ausführen, müssen Sie sicherstellen, dass Ihre Go-Version 1.23 ist. Die Ausgabe ist genau die gleiche:
0 2 1 4 2 6 3 8 4 10
Aber Moment, warum sollten wir hier einen Iterator brauchen? Ist das nicht komplizierter? Schauen wir uns die Unterschiede genauer an.
Auf den ersten Blick scheinen Iteratoren für etwas so Einfaches wie das Transformieren einer Liste etwas überentwickelt zu sein. Aber wenn Sie Benchmarks durchführen, beginnen Sie zu erkennen, warum sie eine Überlegung wert sind!
Lassen Sie uns beide Methoden vergleichen und sehen, wie sie funktionieren:
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) } }
Hier ist das erste Benchmark-Ergebnis:
BenchmarkNormalTransform-8 41292933 29.49 ns/op BenchmarkIteratorTransform-8 1000000000 0.3135 ns/op
Whoa! Das ist ein riesiger Unterschied! Aber Moment mal – hier herrscht ein wenig Ungerechtigkeit. Die NormalTransform-Funktion gibt eine vollständig transformierte Liste zurück, während die IteratorTransform-Funktion nur den Iterator einrichtet, ohne die Liste noch zu transformieren.
Machen wir es fair, indem wir den Iterator vollständig durchlaufen:
func BenchmarkIteratorTransform(b *testing.B) { for i := 0; i < b.N; i++ { for range IteratorTransform(list, transform) { } } }
Jetzt sind die Ergebnisse vernünftiger:
BenchmarkNormalTransform-8 40758822 29.16 ns/op BenchmarkIteratorTransform-8 53967146 22.39 ns/op
Okay, der Iterator ist etwas schneller. Warum? Denn NormalTransform erstellt eine gesamte transformierte Liste im Speicher (auf dem Heap), bevor es sie zurückgibt, während der Iterator die Transformation durchführt, während Sie sie durchlaufen, was Zeit und Speicher spart.
Lesen Sie hier mehr über Stack and Heap.
Die wahre Magie des Iterators entfaltet sich, wenn Sie nicht die gesamte Liste verarbeiten müssen. Vergleichen wir ein Szenario, in dem wir die Nummer 4 erst nach der Transformation der Liste finden möchten:
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 } } } }
Die Ergebnisse sprechen für sich:
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) } }
In diesem Fall ist der Iterator viel schneller! Warum? Da der Iterator nicht die gesamte Liste transformiert, stoppt er, sobald er das gesuchte Ergebnis findet. Andererseits transformiert NormalTransform immer noch die gesamte Liste, auch wenn wir uns nur um ein Element kümmern.
Warum also einen Iterator in Go verwenden?
Iteratoren: Sie sind schnell, flexibel und machen Spaß – wenn man erst einmal den Dreh raus hat!
Das obige ist der detaillierte Inhalt vonIteratoren in Go verstehen: Ein unterhaltsamer Tauchgang!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!