Existe-t-il une boucle foreach dans Go?

566

Existe-t-il une foreachconstruction dans le langage Go? Puis-je parcourir une tranche ou un tableau à l'aide d'un for?

tatsuhirosatou
la source
2
L'utilisation des boucles rangein forest également mentionnée dans la section "Interlude sur les types" (vers la fin) du didacticiel Go.
kostix

Réponses:

851

https://golang.org/ref/spec#For_range

Une instruction "for" avec une clause "range" parcourt toutes les entrées d'un tableau, d'une tranche, d'une chaîne ou d'une mappe, ou les valeurs reçues sur un canal. Pour chaque entrée, il attribue des valeurs d'itération aux variables d'itération correspondantes, puis exécute le bloc.

Par exemple:

for index, element := range someSlice {
    // index is the index where we are
    // element is the element from someSlice for where we are
}

Si vous ne vous souciez pas de l'index, vous pouvez utiliser _:

for _, element := range someSlice {
    // element is the element from someSlice for where we are
}

Le trait de soulignement,, _est l' identificateur vide , un espace réservé anonyme.

davetron5000
la source
7
Dans cet exemple, elementest la valeur de l'élément (une copie) - ce n'est pas l'élément lui-même. Bien que vous puissiez l'assigner à element, cela n'affectera pas la séquence sous-jacente.
nobar
Je sais qu'en Python et C, il est fréquent d'utiliser le soulignement comme fonction de localisation (c'est-à-dire le gettext ). L'utilisation du trait de soulignement causerait-il des problèmes dans Go? Go utilise-t-il même la même bibliothèque pour la localisation?
Sergiy Kolodyazhnyy
3
@SergiyKolodyazhnyy Py docs dit que "la fonction (gettext) est généralement aliasée comme _()dans l'espace de noms local" ce qui est juste par convention , cela ne fait pas partie de la lib de localisation. Le trait de soulignement _est une étiquette valide, et c'est également une convention dans Go (et Python et Scala et d'autres langs) à affecter aux _valeurs de retour que vous n'utiliserez pas. La portée de _dans cet exemple est limitée au corps de la forboucle. Si vous avez une fonction de portée de package, _elle sera alors masquée dans la portée de la boucle for. Il y a quelques paquets pour la localisation, je n'ai vu aucune utilisation _comme nom de fonction.
Davos
150

Go a une foreachsyntaxe similaire à. Il prend en charge les tableaux / tranches, les cartes et les canaux.

Itérer sur un tableau ou une tranche :

// index and value
for i, v := range slice {}

// index only
for i := range slice {}

// value only
for _, v := range slice {}

Itérer sur une carte :

// key and value
for key, value := range theMap {}

// key only
for key := range theMap {}

// value only
for _, value := range theMap {}

Itérer sur un canal :

for v := range theChan {}

Itérer sur un canal équivaut à recevoir d'un canal jusqu'à ce qu'il soit fermé:

for {
    v, ok := <-theChan
    if !ok {
        break
    }
}
Moshe Revah
la source
11
Bien que l'OP ne demande que l'utilisation des tranches, je préfère cette réponse, car la plupart auront également besoin des autres utilisations.
domoarigato
3
distinction importante à propos de l' chanutilisation: le fait de parcourir un canal quittera gracieusement la boucle si l'écrivain ferme le canal à un moment donné. Dans l' for {v := <-theChan} équivalent , il ne sortira pas à la fermeture du canal. Vous pouvez le tester via la deuxième okvaleur de retour. EXEMPLE DE TOUR
colm.anseo
Pensé de même lors de la lecture, for { ... }représente une boucle infinie.
Lévite
13

L'exemple suivant montre comment utiliser l' rangeopérateur dans une forboucle pour implémenter une foreachboucle.

func PrintXml (out io.Writer, value interface{}) error {
    var data []byte
    var err error

    for _, action := range []func() {
        func () { data, err = xml.MarshalIndent(value, "", "  ") },
        func () { _, err = out.Write([]byte(xml.Header)) },
        func () { _, err = out.Write(data) },
        func () { _, err = out.Write([]byte("\n")) }} {
        action();
        if err != nil {
            return err
        }
    }
    return nil;
}

L'exemple parcourt un tableau de fonctions pour unifier la gestion des erreurs pour les fonctions. Un exemple complet est sur le terrain de jeu de Google .

PS: cela montre aussi que les accolades suspendues sont une mauvaise idée pour la lisibilité du code. Astuce: la forcondition se termine juste avant l' action()appel. Évident, non?

ceving
la source
3
Ajoutez un ,et il est plus clair où la forcondition se termine: play.golang.org/p/pcRg6WdxBd - C'est en fait la première fois que je trouve un contre-argument au go fmtstyle, merci!
topskip
@topskip les deux sont valides fmt; choisissez le meilleur :)
Filip Haglund
@FilipHaglund Ce n'est pas la question s'il est valide. Le fait est que l'OMI est plus clair où la condition for se termine dans ce cas particulier ci-dessus.
topskip
9
À mon avis, cette réponse est déraisonnablement complexe pour la question ciblée.
AndreasHassing
@AndreasHassing Comment faire à la place sans introduire de redondance?
ceving
10

Vous pouvez en fait utiliser rangesans référencer ses valeurs de retour en utilisant for rangecontre votre type:

arr := make([]uint8, 5)
i,j := 0,0
for range arr {
    fmt.Println("Array Loop",i)
    i++
}

for range "bytes" {
    fmt.Println("String Loop",j)
    j++
}

https://play.golang.org/p/XHrHLbJMEd

robstarbuck
la source
3
Bon à savoir mais cela ne sera pas utile dans la plupart des cas
Sridhar
D'accord @Sridhar c'est une jolie niche.
robstarbuck
10

Voici l'exemple de code pour savoir comment utiliser foreach dans golang

package main

import (
    "fmt"
)

func main() {

    arrayOne := [3]string{"Apple", "Mango", "Banana"}

    for index,element := range arrayOne{

        fmt.Println(index)
        fmt.Println(element)        

    }   

}

Ceci est un exemple en cours d'exécution https://play.golang.org/p/LXptmH4X_0

Nisal Edu
la source
très bonne explication!
Darlan Dieterich
Parfois, c'est l'exemple le plus simple qui est le plus utile. Merci! Je n'ai rien contre les réponses les plus ésotériques des autres commentateurs - elles illustrent certainement les subtilités d'une programmation Go très idiomatique, au point qu'elles deviennent ... illisibles et difficiles à suivre - mais je préfère votre réponse: elle va directement au cœur avec l'exemple le plus simple possible (qui fonctionne et il est évident pourquoi cela fonctionne).
Gwyneth Llewelyn
4

Oui, plage :

La forme de plage de la boucle for itère sur une tranche ou une carte.

Lors d'une plage sur une tranche, deux valeurs sont renvoyées pour chaque itération. Le premier est l'index et le second est une copie de l'élément à cet index.

Exemple :

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }

    for i := range pow {
        pow[i] = 1 << uint(i) // == 2**i
    }
    for _, value := range pow {
        fmt.Printf("%d\n", value)
    }
}
  • Vous pouvez ignorer l'index ou la valeur en l'affectant à _.
  • Si vous souhaitez uniquement l'index, supprimez entièrement la valeur.
Amitesh
la source
1

Cela peut être évident, mais vous pouvez aligner le tableau comme suit:

package main

import (
    "fmt"
)

func main() {
    for _, element := range [3]string{"a", "b", "c"} {
        fmt.Print(element)
    }
}

les sorties:

abc

https://play.golang.org/p/gkKgF3y5nmt

jpihl
la source