iter.Seq în Go

Tipurile pentru iteratori: Seq produce valori una câte una, Seq2 produce perechi; ambele se parcurg cu for range.

Sintaxă
import "iter"

type Seq[V any] func(yield func(V) bool)
type Seq2[K, V any] func(yield func(K, V) bool)

Ce face iter.Seq

Un iterator în Go este o funcție care primește o altă funcție, numită de obicei yield, și o apelează pentru fiecare valoare. iter.Seq[V] produce valori simple, iter.Seq2[K, V] produce perechi (ca indice–valoare sau cheie–valoare). Din Go 1.23, for v := range seq și for k, v := range seq2 parcurg astfel de funcții direct.

yield returnează false când bucla se oprește mai devreme (break, return). Iteratorul trebuie atunci să se oprească imediat: if !yield(v) { return }. Altfel programul se oprește cu panic.

Valorile sunt produse doar când sunt cerute, deci un iterator poate fi chiar infinit (șirul lui Fibonacci) sau poate citi date treptat. Funcțiile din slices și maps (All, Values, Keys, Collect, Sorted) primesc sau returnează aceste tipuri.

Parametri

Parametru Descriere
yield Funcția apelată pentru fiecare valoare; returnează false când parcurgerea trebuie oprită.

Ce returnează

Sunt tipuri, nu funcții: le folosești ca tip returnat de funcțiile tale care produc valori.

Exemple

Exemplul 1: Un iterator infinit oprit cu break

package main

import (
    "fmt"
    "iter"
)

func fibonacci() iter.Seq[int] {
    return func(yield func(int) bool) {
        a, b := 0, 1
        for {
            if !yield(a) {
                return
            }
            a, b = b, a+b
        }
    }
}

func main() {
    for n := range fibonacci() {
        if n > 100 {
            break
        }
        fmt.Print(n, " ")
    }
    fmt.Println()
}

Rezultat

0 1 1 2 3 5 8 13 21 34 55 89

Exemplul 2: Seq2: rânduri numerotate dintr-un text

package main

import (
    "fmt"
    "iter"
    "strings"
)

func randuri(text string) iter.Seq2[int, string] {
    return func(yield func(int, string) bool) {
        for i, r := range strings.Split(text, "\n") {
            if !yield(i+1, r) {
                return
            }
        }
    }
}

func main() {
    poezie := "Pe lângă plopii fără soț\nAdesea am trecut"
    for nr, rand := range randuri(poezie) {
        fmt.Printf("%d: %s\n", nr, rand)
    }
}

Rezultat

1: Pe lângă plopii fără soț
2: Adesea am trecut

Exemplul 3: Un iterator propriu folosit cu funcțiile din slices

package main

import (
    "fmt"
    "iter"
    "slices"
)

func multipli(n, cati int) iter.Seq[int] {
    return func(yield func(int) bool) {
        for i := 1; i <= cati; i++ {
            if !yield(n * i) {
                return
            }
        }
    }
}

func main() {
    fmt.Println(slices.Collect(multipli(7, 5)))
    fmt.Println(slices.Sorted(multipli(-3, 4)))
}

Rezultat

[7 14 21 28 35]
[-12 -9 -6 -3]

Greșeli frecvente

  • Ignori rezultatul lui yield și continui după break: programul se oprește cu panic: range function continued iteration after function for loop body returned false.
  • Te aștepți ca un iter.Seq să aibă len sau indici: nu este un slice. Strânge valorile cu slices.Collect dacă ai nevoie de ele toate.

Comenzi înrudite

  • iter.Pull: Transformă un iterator „push” într-unul „pull”: ceri singur fiecare valoare cu next() și închizi cu stop().
  • slices.All: Transformă un slice în iterator: All dă perechi (indice, valoare), Values doar valorile, Backward perechile de la coadă la cap.
  • slices.Collect: Collect strânge valorile unui iterator într-un slice nou; AppendSeq le adaugă la finalul unui slice existent.
  • maps.Keys: Returnează un iterator peste cheile (Keys) sau valorile (Values) unui map, în ordine aleatorie.