Maison > développement back-end > Golang > Enregistrez les pièges de la traversée de la boucle de Go

Enregistrez les pièges de la traversée de la boucle de Go

藏色散人
Libérer: 2021-02-19 17:38:23
avant
2371 Les gens l'ont consulté
Ci-dessous

Golang La colonne tutoriel partagera avec la boucle de GO pour utiliser la petite fosse, j'espère que cela sera utile aux amis dans le besoin !

Enregistrez les pièges de la traversée de la boucle de Go

Dans le contrôle de flux de Golang, il existe deux types d'instructions de boucle : for et range.

pour déclaration

1.for 赋值表达式; 关系表达式或逻辑表达式; 赋值表达式 { }

for i := 0; i < 10; i++ {}
Copier après la connexion

2.for 关系表达式或逻辑表达式 { }

n := 10for n > 0 {
 n--}
Copier après la connexion

3.for { }

for {
 fmt.Println("hello world")
}
// 等价于
// for true {
//     fmt.Println("hello world")
// }
Copier après la connexion

instruction range

La plage Golang est similaire à une opération d'itérateur et peut parcourir des tranches, des cartes, des tableaux, des chaînes, etc. Il renvoie (index, valeur) dans les chaînes, les tableaux et les tranches, et (clé, valeur) dans les collections, mais lorsqu'il n'y a qu'une seule valeur de retour, le premier argument est l'index ou la clé.

str := "abc"
for i, char := range str {
    fmt.Printf("%d => %s\n", i, string(char))
}
for i := range str { //只有一个返回值
    fmt.Printf("%d\n", i)
}
nums := []int{1, 2, 3}
for i, num := range nums {
    fmt.Printf("%d => %d\n", i, num)
}
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
    fmt.Printf("%s => %s\n", k, v)
}
for k := range kvs { //只有一个返回值
    fmt.Printf("%s\n", k)
}
// 输出结果
// 0 => a
// 1 => b
// 2 => c
// 0
// 1
// 2
// 0 => 1
// 1 => 2
// 2 => 3
// a => apple
// b => banana
// a
// b
Copier après la connexion

La boucle for, en particulier l'instruction range, est fréquemment utilisée dans le processus de développement quotidien, mais de nombreux développeurs (dont moi-même) tombent souvent dans le piège dans les scénarios suivants.

Scénario 1, utilisation de variables d'itérateur de boucle

Regardons d'abord une erreur évidente :

func main() {
    var out []*int
    for i := 0; i < 3; i++ {
        // i := i
        out = append(out, &i)
    }
    fmt.Println("值:", *out[0], *out[1], *out[2])
    fmt.Println("地址:", out[0], out[1], out[2])
}
// 输出结果
// 值: 3 3 3
// 地址: 0xc000012090 0xc000012090 0xc000012090
Copier après la connexion

Analyse

est une variable de tableau de pointeurs entiers. Dans la boucle for, une variable out est déclarée. Chaque boucle ajoute l'adresse de i à la tranche i, mais chaque fois qu'elle est ajoutée, elle est en réalité ajoutée. out variables, donc ce que nous ajoutons est la même adresse, et la valeur finale de l'adresse est 3. i

Approche correcte

Débloquez les commentaires

dans le code et recréez une nouvelle // i := i variable à chaque fois dans la boucle. i


Regardons une erreur plus subtile :

func main() {
    a1 := []int{1, 2, 3}
    a2 := make([]*int, len(a1))

    for i, v := range a1 {
        a2[i] = &v
    }

    fmt.Println("值:", *a2[0], *a2[1], *a2[2])
    fmt.Println("地址:", a2[0], a2[1], a2[2])
}
// 输出结果
// 值: 3 3 3
// 地址: 0xc000012090 0xc000012090 0xc000012090
Copier après la connexion

Analyse

La plupart des gens attribuent des valeurs aux variables ici

Parfois Je marche sur le piège car il est relativement secret. En fait, la situation est la même que ci-dessus Lorsque range traverse le type valeur, le range est une variable locale. Elle ne sera déclarée et initialisée qu'une seule fois. puis réaffecté à chaque fois qu'il boucle pour écraser le précédent, donc lors de l'attribution de valeurs à v, elles ont en fait toutes la même adresse a2[i], et la valeur finale de &v est la valeur de la dernière. élément de v, qui vaut 3. a1

Approche correcte

Passez le pointeur d'origine lors de l'affectation, c'est-à-dire a2[i]a2[i] = &a1[i]② Créez une variable temporaire
 ; 🎜>t := v③ Fermeture (identique au principe ②), a2[i] = &t
func(v int) { a2[i] = &v }(v)

Plus secrètement :
func main() {
    var out [][]int
    for _, i := range [][1]int{{1}, {2}, {3}} {
        out = append(out, i[:])
    }
    fmt.Println("Values:", out)}// 输出结果// [[3] [3] [3]]
Copier après la connexion

Le principe est le même, peu importe le nombre de fois qu'il est parcouru ,

Toujours écrasé par la valeur de ce parcours

i[:]

Scénario 2, utiliser des goroutines dans le corps de la boucle
func main() {
    values := []int{1, 2, 3}
    wg := sync.WaitGroup{}
    for _, val := range values {
        wg.Add(1)
        go func() {
            fmt.Println(val)
            wg.Done()
        }()
    }
    wg.Wait()}// 输出结果// 3// 3// 3
Copier après la connexion

Analyse

Pour la coroutine principale, la boucle se termine rapidement et chaque coroutine ne peut commencer à s'exécuter qu'à ce moment-là, la valeur de
a été parcourue jusqu'à la dernière, donc chaque coroutine a une sortie <🎜. >. (Si les données de parcours sont énormes et que le parcours de la coroutine principale prend beaucoup de temps, la sortie de la goroutine sera basée sur la valeur de

à ce moment-là, donc le résultat de sortie n'est pas nécessairement le même à chaque fois.) val3valSolution

①Utiliser une variable temporaire

for _, val := range values {
    wg.Add(1)
    val := val    go func() {
        fmt.Println(val)
        wg.Done()
    }()}
Copier après la connexion
②Utiliser la fermeture

for _, val := range values {
    wg.Add(1)
    go func(val int) {
        fmt.Println(val)
        wg.Done()
    }(val)}
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:learnku.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal