Aller au contenu

Les Itérateurs en Go

Les itérateurs sont apparus tout récemment dans la version 1.23 de Go. Ils font suite à la généricité introduite en Go 1.18.

Nous pouvons illustrer les itérateurs avec un exemple simple qui génère les n premiers éléments de la fameuse suite de Fibonacci :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
    "fmt"
)

func Fibonacci(n int) func(func(int) bool) {
    return func(yield func(int) bool) {
        f0, f1 := 0, 1
        for range n {
            if !yield(f0) {
                return
            }
            f0, f1 = f1, f0+f1
        }
    }
}

func main() {
    for i := range Fibonacci(20) {
        fmt.Println(i)
    }
}
Affichage
0
1
1
2
3
5
8
13
...
2584
4181

La fonction Fibonacci retourne une fonction (l’itérateur) qui prend en argument une fonction (communément appelé yield) qui retourne un booléen.

Au passage, notez l’utilisation de l’instruction for range n de la ligne 10. Cette construction, introduite avec Go 1.22, remplace avantageusement une boucle for i := 0; i < n; i++ classique.

Lorsque l’itérateur est utilisé dans une boucle for range (ligne 20), Go crée automatiquement la fonction yield avec le corps de la boucle (ligne 21). À chaque itération, la fonction yield est appelée avec la valeur courante de la suite de Fibonacci. Si yield retourne false, l’itérateur s’arrête.

On obtient le même résultat en appelant explicitement l’itérateur avec une fonction anonyme :

Fibonacci(10)(func(i int) bool {
    fmt.Println(i)
    return true
})

mais l’utilisation dans une boucle for range est plus élégante.

Pour plus d’informations, vous pouvez consulter les articles suivants :

Etudiez aussi le paquetage iter qui fournit des itérateurs pour les types de données courants.

Le paquetage iter permet de réécrire l’itérateur Fibonacci de la manière suivante :

func Fibonacci(n int) iter.Seq[int] {
    return func(yield func(int) bool) {
        f0, f1 := 0, 1
        for range n {
            if !yield(f0) {
                return
            }
            f0, f1 = f1, f0+f1
        }
    }
}

À partir de Go 1.23, les paquetage slices et maps de la bibliothèque standard ont été enrichis avec des fonctions utilisant des itérateurs.

Si on reprend l’exemple de la map avec les coordonnées géographiques, nous pouvons maintenant utiliser un itérateur pour parcourir toutes les clés et les valeurs avec la fonction maps.All :

package main

import (
    "fmt"
    "maps"
)

type Coordinate struct {
    Lat, Long float64
}

var m = map[string]Coordinate{
    "HEIA-FR": {46.7926, 7.15993},
    "Google":  {37.42202, -122.08408},
}

func main() {
    for k, v := range maps.All(m) {
        fmt.Println(k, v)
    }
}
Affichage
Google {37.42202 -122.08408}
HEIA-FR {46.7926 7.15993}

Et si je ne veux que les clés, je peux utiliser la fonction maps.Keys :

func main() {
    for k := range maps.Keys(m) {
        fmt.Println(k)
    }
}
Affichage
HEIA-FR
Google