173 votes

Modifier les valeurs lors de l'itération en golang

Supposons que j'ai de ces types :

type Attribute struct {
    Key, Val string
}
type Node struct {
    Attr []Attribute
}

et que je veux pour itérer sur mon nœud attributs de les changer.

J'aurais aimé être capable de faire

for _, attr := range n.Attr {
    if attr.Key == "href" {
        attr.Val = "something"
    }
}

mais comme attr n'est pas un pointeur, ce ne serait pas de travail et que j'ai à faire

for i, attr := range n.Attr {
    if attr.Key == "href" {
        n.Attr[i].Val = "something"
    }
}

Est-il plus simple et de plus rapide ? Est-il possible d'obtenir directement des points de range ?

Évidemment, je ne veux pas changer les structures, juste pour l'itération et plus prolixe solutions sont des solutions ne

172voto

nemo Points 13983

Non, l'abréviation que vous souhaitez n'est pas possible.

La raison pour cela est qu' range copie les valeurs de la tranche que vous avez à parcourir. La spécification au sujet de gamme dit:

Range expression                          1st value             2nd value (if 2nd variable is present)
array or slice  a   [n]E, *[n]E, or []E   index    i  int       a[i]       E

Ainsi, la gamme des utilisations a[i] comme deuxième valeur pour les tableaux/tranches, ce qui signifie que la la valeur est copiée, faire le valeur d'origine intouchable.

Ce comportement est illustré par le code suivant:

x := make([]int, 3)

x[0], x[1], x[2] = 1, 2, 3

for i, val := range x {
    println(&x[i], "vs.", &val)
}

Le code tirages vous complètement différents emplacements de mémoire de la valeur de l'aire de répartition et le réel la valeur de la tranche:

0xf84000f010 vs. 0x7f095ed0bf68
0xf84000f014 vs. 0x7f095ed0bf68
0xf84000f018 vs. 0x7f095ed0bf68

Donc, la seule chose que vous pouvez faire est d'utiliser des pointeurs ou de l'index, comme déjà proposé par jnml et peterSO.

39voto

peterSO Points 25725

Vous semblez être de demander quelque chose équivalent à ceci:

package main

import "fmt"

type Attribute struct {
    Key, Val string
}
type Node struct {
    Attr []Attribute
}

func main() {

    n := Node{
        []Attribute{
            {"key", "value"},
            {"href", "http://www.google.com"},
        },
    }
    fmt.Println(n)

    for i := 0; i < len(n.Attr); i++ {
        attr := &n.Attr[i]
        if attr.Key == "href" {
            attr.Val = "something"
        }
    }

    fmt.Println(n)
}

Sortie:

{[{key value} {href http://www.google.com}]}
{[{key value} {href something}]}

Cela évite la création d'un-peut-être grand--copie de type Attribute valeurs, au détriment de la tranche limites des contrôles. Dans votre exemple, tapez Attribute est relativement faible, les deux string tranche de références: 2 * 3 * 8 = 48 octets sur une architecture 64 bits de la machine.

Vous pouvez aussi tout simplement écrire:

for i := 0; i < len(n.Attr); i++ {
    if n.Attr[i].Key == "href" {
        n.Attr[i].Val = "something"
    }
}

Mais, la manière d'obtenir un résultat équivalent avec un range de la clause, ce qui crée une copie, mais minimise la tranche limites des contrôles, est:

for i, attr := range n.Attr {
    if attr.Key == "href" {
        n.Attr[i].Val = "something"
    }
}

33voto

Anonymous Points 7804

J'aimerais adapter votre dernière suggestion et de l'utilisation de l'indice de la seule version de la gamme.

for i := range n.Attr {
    if n.Attr[i].Key == "href" {
        n.Attr[i].Val = "something"
    }
}

Il semble plus simple pour moi de me référer à l' n.Attr[i] explicitement à la fois la ligne qui teste Key et la ligne qui définit Val, plutôt que d'utiliser attr pour l'un et de l' n.Attr[i] pour les autres.

15voto

zzzz Points 23017

Par exemple:

package main

import "fmt"

type Attribute struct {
        Key, Val string
}

type Node struct {
        Attr []*Attribute
}

func main() {
        n := Node{[]*Attribute{
                &Attribute{"foo", ""},
                &Attribute{"href", ""},
                &Attribute{"bar", ""},
        }}

        for _, attr := range n.Attr {
                if attr.Key == "href" {
                        attr.Val = "something"
                }
        }

        for _, v := range n.Attr {
                fmt.Printf("%#v\n", *v)
        }
}

Aire de jeux


Sortie

main.Attribute{Key:"foo", Val:""}
main.Attribute{Key:"href", Val:"something"}
main.Attribute{Key:"bar", Val:""}

Autre approche:

package main

import "fmt"

type Attribute struct {
        Key, Val string
}

type Node struct {
        Attr []Attribute
}

func main() {
        n := Node{[]Attribute{
            {"foo", ""},
            {"href", ""},
            {"bar", ""},
        }}

        for i := range n.Attr {
                attr := &n.Attr[i]
                if attr.Key == "href" {
                        attr.Val = "something"
                }
        }

        for _, v := range n.Attr {
                fmt.Printf("%#v\n", v)
        }
}

Aire de jeux


Sortie:

main.Attribute{Key:"foo", Val:""}
main.Attribute{Key:"href", Val:"something"}
main.Attribute{Key:"bar", Val:""}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X