Comment construire des boucles pour Go

introduction

En programmation informatique, unloop est une structure de code qui boucle pour exécuter à plusieurs reprises un morceau de code, souvent jusqu'à ce qu'une condition soit remplie. L'utilisation de boucles dans la programmation informatique vous permet d'automatiser et de répéter des tâches similaires à plusieurs reprises. Imaginez si vous aviez une liste de fichiers à traiter ou si vous vouliez compter le nombre de lignes d'un article. Vous utiliseriez une boucle dans votre code pour résoudre ces types de problèmes.

Dans Go, une bouclefor implémente l'exécution répétée de code basée sur un compteur de boucle ou une variable de boucle. Contrairement à d'autres langages de programmation qui ont plusieurs constructions en boucle telles quewhile,do, etc., Go n'a que la bouclefor. Cela permet de rendre votre code plus clair et plus lisible, car vous n'avez pas à vous soucier de plusieurs stratégies pour obtenir la même construction en boucle. Cette lisibilité améliorée et la charge cognitive réduite au cours du développement rendront également votre code moins sujet aux erreurs que dans d'autres langues.

Dans ce didacticiel, vous apprendrez comment fonctionne la bouclefor de Go, y compris les trois principales variantes de son utilisation. Nous allons commencer par montrer comment créer différents types de bouclesfor, puis comment effectuer une boucle sursequential data types in Go. Nous finirons par expliquer comment utiliser les boucles imbriquées.

Déclarer des boucles ForClause et Condition

Afin de prendre en compte une variété de cas d'utilisation, il existe trois façons distinctes de créer des bouclesfor dans Go, chacune avec ses propres capacités. Il s'agit de créer une bouclefor avec unCondition, unForClause ou unRangeClause. Dans cette section, nous expliquerons comment déclarer et utiliser les variantes ForClause et Condition.

Voyons d'abord comment utiliser une bouclefor avec ForClause.

UnForClause loop est défini comme ayant uninitial statement, suivi d'uncondition, puis d'unpost statement. Celles-ci sont organisées dans la syntaxe suivante:

for [ Initial Statement ] ; [ Condition ] ; [ Post Statement ] {
    [Action]
}

Pour expliquer ce que font les composants précédents, examinons une bouclefor qui s'incrémente dans une plage de valeurs spécifiée à l'aide de la syntaxe ForClause:

for i := 0; i < 5; i++ {
    fmt.Println(i)
}

Coupons cette boucle et identifions chaque partie.

La première partie de la boucle esti := 0. Ceci est la déclaration initiale:

for i := 0; i < 5; i++ {
    fmt.Println(i)
}

Il indique que nous déclarons une variable appeléei et que nous définissons la valeur initiale sur0.

Suivant est la condition:

for i := 0; i < 5; i++ {
    fmt.Println(i)
}

Dans cette condition, nous avons déclaré que sii est inférieur à la valeur de5, la boucle doit continuer à boucler.

Enfin, nous avons la déclaration de poste:

for i := 0; i < 5; i++ {
    fmt.Println(i)
}

Dans l'instruction post, nous incrémentons la variable de bouclei de un à chaque fois qu'une itération se produit à l'aide de l'opérateuri++increment.

Lorsque nous exécutons ce programme, la sortie ressemble à ceci:

Output0
1
2
3
4

La boucle a été exécutée 5 fois. Initialement, il définissaiti sur0, puis vérifiait sii était inférieur à5. Puisque la valeur dei était inférieure à5, la boucle s'est exécutée et l'action defmt.Println(i) a été exécutée. Une fois la boucle terminée, l'instruction post dei++ a été appelée et la valeur dei a été incrémentée de 1.

[.note] #Note: Gardez à l'esprit qu'en programmation, nous avons tendance à commencer à l'index 0, c'est pourquoi bien que 5 nombres soient imprimés, ils vont de 0 à 4.
#

Nous ne sommes pas limités à commencer à 0 ou à une valeur spécifiée. Nous pouvons attribuer n'importe quelle valeur à notre relevé initial et nous arrêter à n'importe quelle valeur de notre relevé post. Cela nous permet de créer toute plage désirée à parcourir en boucle:

for i := 20; i < 25; i++ {
    fmt.Println(i)
}

Ici, l'itération passe de 20 (inclus) à 25 (exclusif), ainsi le résultat ressemble à ceci:

Output20
21
22
23
24

Nous pouvons également utiliser notre instruction post pour incrémenter différentes valeurs. Ceci est similaire àstep dans d'autres langues:

Tout d’abord, utilisons une instruction post avec une valeur positive:

for i := 0; i < 15; i += 3 {
    fmt.Println(i)
}

Dans ce cas, la bouclefor est configurée pour que les nombres de 0 à 15 s'impriment, mais à un incrément de 3, de sorte que seul un nombre sur trois soit imprimé, comme ceci:

Output0
3
6
9
12

Nous pouvons également utiliser une valeur négative pour notre argument post statement pour effectuer une itération en arrière, mais nous devrons ajuster nos arguments initiaux d'instruction et de condition en conséquence:

for i := 100; i > 0; i -= 10 {
    fmt.Println(i)
}

Ici, nous définissonsi à une valeur initiale de100, utilisons la condition dei < 0 pour s'arrêter à0, et l'instruction post décrémente la valeur de 10 avec le-= opérateur. La boucle commence à100 et se termine à0, diminuant de 10 à chaque itération. Nous pouvons voir que cela se produit dans la sortie:

Output100
90
80
70
60
50
40
30
20
10

Vous pouvez également exclure l'instruction initiale et l'instruction post de la syntaxefor et n'utiliser que la condition. C'est ce qu'on appelle unCondition loop:

i := 0
for i < 5 {
    fmt.Println(i)
    i++
}

Cette fois, nous avons déclaré la variablei séparément de la bouclefor dans la ligne de code précédente. La boucle n'a qu'une clause de condition qui vérifie sii est inférieur à5. Tant que la condition est évaluée àtrue, la boucle continuera à itérer.

Parfois, vous pouvez ne pas savoir le nombre d'itérations dont vous aurez besoin pour effectuer une tâche donnée. Dans ce cas, vous pouvez omettre toutes les instructions et utiliser le mot clébreak pour quitter l'exécution:

for {
    if someCondition {
        break
    }
    // do action here
}

Un exemple de ceci peut être si nous lisons à partir d'une structure de taille indéterminée comme unbuffer et nous ne savons pas quand nous aurons fini de lire:

buffer.go

package main

import (
    "bytes"
    "fmt"
    "io"
)

func main() {
    buf := bytes.NewBufferString("one\ntwo\nthree\nfour\n")

    for {
        line, err := buf.ReadString('\n')
        if err != nil {
            if err == io.EOF {

                fmt.Print(line)
                break
            }
            fmt.Println(err)
            break
        }
        fmt.Print(line)
    }
}

Dans le code précédent,buf :=bytes.NewBufferString("one two three four ") déclare un tampon avec certaines données. Parce que nous ne savons pas quand le tampon finira de lire, nous créons une bouclefor sans clause. À l'intérieur de la bouclefor, nous utilisonsline, err := buf.ReadString(' ') pour lire une ligne du tampon et vérifier s'il y a eu une erreur de lecture du tampon. Si tel était le cas, nous corrigeons l'erreur etuse the break keyword to exit the for loop. Avec ces pointsbreak, vous n'avez pas besoin d'inclure une condition pour arrêter la boucle.

Dans cette section, nous avons appris à déclarer une boucle ForClause et à l'utiliser pour parcourir une plage de valeurs connue. Nous avons également appris à utiliser une boucle de condition pour itérer jusqu'à ce qu'une condition spécifique soit remplie. Nous verrons ensuite comment RangeClause est utilisé pour effectuer une itération à travers des types de données séquentiels.

Boucle à travers les types de données séquentielles avec RangeClause

Il est courant dans Go d'utiliser les bouclesfor pour parcourir les éléments de types de données séquentielles ou de collecte commeslices, arrays etstrings. Pour rendre cela plus facile, nous pouvons utiliser une bouclefor avec la syntaxeRangeClause. Alors que vous pouvez parcourir des types de données séquentiels à l'aide de la syntaxe ForClause, RangeClause est plus propre et plus facile à lire.

Avant d’envisager l’utilisation de RangeClause, voyons comment on peut parcourir une tranche en utilisant la syntaxe ForClause:

main.go

package main

import "fmt"

func main() {
    sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}

    for i := 0; i < len(sharks); i++ {
        fmt.Println(sharks[i])
    }
}

En cours d'exécution, vous obtiendrez le résultat suivant, en imprimant chaque élément de la tranche:

Outputhammerhead
great white
dogfish
frilled
bullhead
requiem

Utilisons maintenant RangeClause pour effectuer le même ensemble d’actions:

main.go

package main

import "fmt"

func main() {
    sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}

    for i, shark := range sharks {
        fmt.Println(i, shark)
    }
}

Dans ce cas, nous imprimons chaque élément de la liste. Bien que nous ayons utilisé les variablesi etshark, nous aurions pu appeler la variable n'importe quel autrevalid variable name et nous obtiendrions le même résultat:

Output0 hammerhead
1 great white
2 dogfish
3 frilled
4 bullhead
5 requiem

Lorsque vous utilisezrange sur une tranche, il renverra toujours deux valeurs. La première valeur sera l'index dans lequel se trouve l'itération actuelle de la boucle, et la seconde est la valeur à cet index. Dans ce cas, pour la première itération, l'index était0 et la valeur étaithammerhead.

Parfois, nous ne voulons que la valeur à l'intérieur des éléments de la tranche, pas l'index. Si nous modifions le code précédent pour n'imprimer que la valeur, nous recevrons une erreur de compilation:

main.go

package main

import "fmt"

func main() {
    sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}

    for i, shark := range sharks {
        fmt.Println(shark)
    }
}
Outputsrc/range-error.go:8:6: i declared and not used

Puisquei est déclaré dans la bouclefor, mais jamais utilisé, le compilateur répondra avec l'erreur dei declared and not used. C’est la même erreur que vous recevrez dans Go chaque fois que vous déclarez une variable et ne l’utilisez pas.

Pour cette raison, Go a leblank identifier qui est un trait de soulignement (_). Dans une bouclefor, vous pouvez utiliser l'identificateur vide pour ignorer toute valeur renvoyée par le mot clérange. Dans ce cas, nous voulons ignorer l'index, qui est le premier argument renvoyé.

main.go

package main

import "fmt"

func main() {
    sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}

    for _, shark := range sharks {
        fmt.Println(shark)
    }
}
Outputhammerhead
great white
dogfish
frilled
bullhead
requiem

Cette sortie montre que la bouclefor a parcouru la tranche de chaînes et imprimé chaque élément de la tranche sans l'index.

Vous pouvez également utiliserrange pour ajouter des éléments à une liste:

main.go

package main

import "fmt"

func main() {
    sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"}

    for range sharks {
        sharks = append(sharks, "shark")
    }

    fmt.Printf("%q\n", sharks)
}
Output['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem', 'shark', 'shark', 'shark', 'shark', 'shark', 'shark']

Ici, nous avons ajouté une chaîne d'espace réservé de"shark" pour chaque élément de la longueur de la tranchesharks.

Notez que nous n'avons pas eu besoin d'utiliser l'identifiant vierge_ pour ignorer les valeurs renvoyées par l'opérateurrange. Go nous permet d'omettre la totalité de la partie déclaration de l'instructionrange si nous n'avons pas besoin d'utiliser l'une ou l'autre des valeurs de retour.

Nous pouvons également utiliser l'opérateurrange pour renseigner les valeurs d'une tranche:

main.go

package main

import "fmt"

func main() {
    integers := make([]int, 10)
    fmt.Println(integers)

    for i := range integers {
        integers[i] = i
    }

    fmt.Println(integers)
}

Dans cet exemple, la trancheintegers est initialisée avec dix valeurs vides, mais la bouclefor définit toutes les valeurs de la liste comme ceci:

Output[0 0 0 0 0 0 0 0 0 0]
[0 1 2 3 4 5 6 7 8 9]

La première fois que nous imprimons la valeur de la trancheintegers, nous voyons tous les zéros. Ensuite, nous parcourons chaque index et définissons la valeur sur l’index actuel. Ensuite, lorsque nous imprimons la valeur deintegers une deuxième fois, montrant qu'ils ont tous maintenant une valeur de0 à9.

Nous pouvons également utiliser l'opérateurrange pour parcourir chaque caractère d'une chaîne:

main.go

package main

import "fmt"

func main() {
    sammy := "Sammy"

    for _, letter := range sammy {
        fmt.Printf("%c\n", letter)
    }
}
OutputS
a
m
m
y

Lors d'une itération sur unmap,range renverra à la fois leskey et lesvalue:

main.go

package main

import "fmt"

func main() {
    sammyShark := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"}

    for key, value := range sammyShark {
        fmt.Println(key + ": " + value)
    }
}
Outputcolor: blue
location: ocean
name: Sammy
animal: shark

[.note] #Note: Il est important de noter que l'ordre dans lequel une carte retourne est aléatoire. Chaque fois que vous exécutez ce programme, vous pouvez obtenir un résultat différent.
#

Maintenant que nous avons appris à parcourir des données séquentielles avec des bouclesrangefor, voyons comment utiliser des boucles à l'intérieur des boucles.

Nested For Loops

Les boucles peuvent être imbriquées dans Go, comme avec d'autres langages de programmation. Nesting est lorsque nous avons une construction à l'intérieur d'une autre. Dans ce cas, une boucle imbriquée est une boucle qui se produit dans une autre boucle. Celles-ci peuvent être utiles lorsque vous souhaitez effectuer une action en boucle sur chaque élément d'un ensemble de données.

Les boucles imbriquées sont structurellement similaires ànested if statements. Ils sont construits comme suit:

for {
    [Action]
    for {
        [Action]
    }
}

Le programme rencontre d'abord la boucle externe et exécute sa première itération. Cette première itération déclenche la boucle interne imbriquée, qui se termine ensuite à son terme. Ensuite, le programme retourne au sommet de la boucle externe, terminant la deuxième itération et déclenchant à nouveau la boucle imbriquée. Là encore, la boucle imbriquée s’achève et le programme retourne au début de la boucle externe jusqu’à ce que la séquence soit terminée ou jusqu’à ce qu'une interruption ou une autre instruction interrompe le processus.

Implémentons une bouclefor imbriquée afin que nous puissions regarder de plus près. Dans cet exemple, la boucle externe parcourra une tranche d'entiers appeléenumList, et la boucle interne parcourra une tranche de chaînes appeléealphaList.

main.go

package main

import "fmt"

func main() {
    numList := []int{1, 2, 3}
    alphaList := []string{"a", "b", "c"}

    for _, i := range numList {
        fmt.Println(i)
        for _, letter := range alphaList {
            fmt.Println(letter)
        }
    }
}

Lorsque nous exécuterons ce programme, nous recevrons le résultat suivant:

Output1
a
b
c
2
a
b
c
3
a
b
c

La sortie montre que le programme termine la première itération de la boucle externe en imprimant1, qui déclenche ensuite l'achèvement de la boucle interne, en imprimant lesa,b,c consécutivement . Une fois la boucle interne terminée, le programme retourne en haut de la boucle externe, imprime2, puis imprime à nouveau la boucle interne dans son intégralité (a,b,c) s), etc.

Les bouclesfor imbriquées peuvent être utiles pour parcourir des éléments dans des tranches composées de tranches. Dans une tranche composée de tranches, si nous n'utilisons qu'une seule bouclefor, le programme affichera chaque liste interne comme un élément:

main.go

package main

import "fmt"

func main() {
    ints := [][]int{
        []int{0, 1, 2},
        []int{-1, -2, -3},
        []int{9, 8, 7},
    }

    for _, i := range ints {
        fmt.Println(i)
    }
}
Output[0 1 2]
[-1 -2 -3]
[9 8 7]

Afin d'accéder à chaque élément individuel des tranches internes, nous allons implémenter une bouclefor imbriquée:

main.go

package main

import "fmt"

func main() {
    ints := [][]int{
        []int{0, 1, 2},
        []int{-1, -2, -3},
        []int{9, 8, 7},
    }

    for _, i := range ints {
        for _, j := range i {
            fmt.Println(j)
        }
    }
}
Output0
1
2
-1
-2
-3
9
8
7

Lorsque nous utilisons ici une bouclefor imbriquée, nous pouvons parcourir les éléments individuels contenus dans les tranches.

Conclusion

Dans ce didacticiel, nous avons appris à déclarer et à utiliser les bouclesfor pour résoudre des tâches répétitives dans Go. Nous avons également appris les trois différentes variantes d'une bouclefor et quand les utiliser. Pour en savoir plus sur les bouclesfor et comment en contrôler le flux, lisezUsing Break and Continue Statements When Working with Loops in Go.