Go-Teratoren verstehen

王林
Freigeben: 2024-08-18 06:32:06
Original
970 Leute haben es durchsucht

Understanding Go terators

Viele Leute scheinen durch neu hinzugefügte Iteratoren in Go verwirrt zu sein, deshalb habe ich beschlossen, einen weiteren Artikel zu schreiben, um sie so einfach wie möglich zu erklären.

Wie werden sie von Go genannt?

Zuerst denke ich, dass es wichtig ist zu verstehen, wie Iteratoren überhaupt von Go aufgerufen und verwendet werden, und es ist eigentlich ziemlich einfach. Nehmen wir den Slices.All-Iterator als Beispiel. So würden Sie diesen Iterator normalerweise verwenden:

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
}
Nach dem Login kopieren

Und so sieht es tatsächlich aus:

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
}
Nach dem Login kopieren

Was passiert, ist, dass der Schleifenkörper „verschoben“ wird, um eine Funktion zu erhalten, die an den Iterator übergeben wird, während continue und break so umgewandelt werden, dass sie „true“ bzw. „false“ zurückgeben. return true wird auch am Ende der Schleife hinzugefügt, um zu signalisieren, dass wir das nächste Element erhalten möchten, sofern zuvor keine andere Entscheidung getroffen wurde.

Dies ist keine genaue Darstellung dessen, was der Compiler tut, und ich habe die Go-Implementierung nicht überprüft, um dies zu überprüfen, aber sie liefern aufgrund meiner Beobachtungen gleichwertige Ergebnisse.

So erstellen Sie Ihren eigenen Iterator und dessen Ausführung

Da Sie nun verstehen, wie sie aufgerufen werden, und erkennen, wie einfach es tatsächlich ist, wird es viel einfacher sein, zu verstehen, wie Sie Ihren eigenen Iterator und seine Ausführung erstellen.

Lassen Sie uns einen Debug-Iterator erstellen, der Debug-Meldungen für jeden Schritt der Iterator-Implementierung druckt, der alle Elemente im Slice durchläuft (slices.All-Funktionalität).

Zuerst werde ich eine kleine Hilfsfunktion erstellen, um eine Meldung mit der aktuellen Ausführungszeit abzumelden.

import (
    "fmt"
    "time"
)

var START time.Time = time.Now()

func logt(message string) {
    fmt.Println(time.Since(START), message)
}
Nach dem Login kopieren

Zurück zum 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")
        }
    }
}
Nach dem Login kopieren

Ich habe einige Debug-Druckanweisungen hinzugefügt, damit wir die Ausführungsreihenfolge des Iterators besser sehen können und wie er auf verschiedene Schlüsselwörter wie break und continue reagiert.

Zuletzt verwenden wir den implementierten Iterator:

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")
    }
}
Nach dem Login kopieren

Gibt uns die Ausgabe:

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
Nach dem Login kopieren

Dieses Beispiel zeigt ziemlich gut, wie Iteratoren funktionieren und ausgeführt werden. Bei Verwendung eines Iterators in einer Bereichsschleife werden alle Anweisungen im Schleifenblock gewissermaßen in eine Funktion namens yield „verschoben“. Wenn wir yield aufrufen, bitten wir go runtime im Wesentlichen darum, alles auszuführen, was sich im Schleifenblock mit dem folgenden Wert für diese Iteration befindet. Aus diesem Grund wird yield auch blockiert, wenn der Schleifenkörper blockiert wird. Falls die Laufzeit feststellt, dass diese Schleifeniteration gestoppt werden soll, gibt yield „false“ zurück. Dies kann passieren, wenn das Schlüsselwort „break“ während der Ausführung des Schleifenblocks erfüllt wird. In diesem Fall sollten wir yield nicht mehr aufrufen. Andernfalls sollten wir weiterhin yield aufrufen.

Vollständiger Code:

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
    //  })
}
Nach dem Login kopieren

Das obige ist der detaillierte Inhalt vonGo-Teratoren verstehen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!