Iteratori in Go
A partire da Go 1.23, il linguaggio supporta gli iteratori come funzionalità nativa. Gli iteratori permettono di definire sequenze personalizzate utilizzabili con il ciclo for range.
Concetto di Iteratore
Un iteratore in Go è una funzione che accetta una funzione di yield come parametro e la chiama per ogni elemento della sequenza:
// iter.Seq[V] è il tipo per iteratori a singolo valore
type Seq[V any] func(yield func(V) bool)
// iter.Seq2[K, V] è il tipo per iteratori con chiave-valore
type Seq2[K, V any] func(yield func(K, V) bool)
Creare un Iteratore Semplice
package main
import "fmt"
// Conta restituisce un iteratore che produce numeri da 0 a n-1
func Conta(n int) func(yield func(int) bool) {
return func(yield func(int) bool) {
for i := 0; i < n; i++ {
if !yield(i) {
return
}
}
}
}
func main() {
for v := range Conta(5) {
fmt.Println(v)
}
// Output: 0, 1, 2, 3, 4
}
Iteratore con Chiave-Valore (Seq2)
// Enumera aggiunge un indice a ogni elemento di uno slice
func Enumera[T any](s []T) func(yield func(int, T) bool) {
return func(yield func(int, T) bool) {
for i, v := range s {
if !yield(i, v) {
return
}
}
}
}
func main() {
nomi := []string{"Alice", "Bob", "Carlo"}
for i, nome := range Enumera(nomi) {
fmt.Printf("%d: %s\n", i, nome)
}
}
Pacchetto slices con Iteratori
Go 1.23 ha arricchito il pacchetto slices con funzioni che restituiscono iteratori:
import "slices"
numeri := []int{3, 1, 4, 1, 5, 9}
// slices.All restituisce un iteratore indice-valore
for i, v := range slices.All(numeri) {
fmt.Println(i, v)
}
// slices.Values restituisce un iteratore solo-valore
for v := range slices.Values(numeri) {
fmt.Println(v)
}
// slices.Backward itera in ordine inverso
for i, v := range slices.Backward(numeri) {
fmt.Println(i, v)
}
// slices.Sorted ordina e restituisce un iteratore
for v := range slices.Sorted(slices.Values(numeri)) {
fmt.Println(v)
}
Pacchetto maps con Iteratori
import "maps"
capitali := map[string]string{
"Italia": "Roma",
"Francia": "Parigi",
}
// maps.Keys restituisce un iteratore sulle chiavi
for chiave := range maps.Keys(capitali) {
fmt.Println(chiave)
}
// maps.Values restituisce un iteratore sui valori
for valore := range maps.Values(capitali) {
fmt.Println(valore)
}
Iteratore Personalizzato: Fibonacci
func Fibonacci(max int) func(yield func(int) bool) {
return func(yield func(int) bool) {
a, b := 0, 1
for a <= max {
if !yield(a) {
return
}
a, b = b, a+b
}
}
}
func main() {
for n := range Fibonacci(100) {
fmt.Println(n)
}
// 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
}
Pull Iterator
Il pacchetto iter fornisce anche Pull per trasformare un push iterator in un pull iterator:
import "iter"
next, stop := iter.Pull(Conta(5))
defer stop()
v1, ok := next() // 0, true
v2, ok := next() // 1, true
Conclusione
Gli iteratori di Go 1.23 sono una potente aggiunta al linguaggio che permette di creare sequenze personalizzate composabili e utilizzabili con for range. Combinati con i pacchetti slices, maps e iter, offrono un modo idiomatico e performante per lavorare con collezioni di dati.