181

これらのタイプがあるとしましょう:

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

そして、ノードの属性を反復して変更したいと考えています。

私はできることが大好きだったでしょう:

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

しかしattr、ポインタではないので、これはうまくいかず、私はしなければなりません:

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

もっと簡単で速い方法はありますか?からポインタを直接取得することは可能rangeですか?

明らかに、反復のためだけに構造を変更したくありません。より詳細なソリューションは解決策ではありません。

4

4 に答える 4

182

いいえ、ご希望の省略形はできません。

この理由は、range反復しているスライスから値をコピーするためです。範囲に関する仕様は次のように述べています。

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

したがって、range はa[i]配列/スライスの 2 番目の値として使用します。これは、値がコピーされ、元の値を変更できないことを効果的に意味します。

この動作は、次のコードによって示されます。

x := make([]int, 3)

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

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

コードは、範囲からの値とスライス内の実際の値に対して完全に異なるメモリ位置を出力します。

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

したがって、jnml と peterSO によって既に提案されているように、ポインターまたはインデックスを使用することしかできません。

于 2013-04-11T15:11:15.713 に答える
41

あなたはこれと同等のものを求めているようです:

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)
}

出力:

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

Attributeこれにより、スライス境界チェックを犠牲にして、タイプ値の (場合によっては大きな) コピーの作成が回避されます。あなたの例では、タイプAttributeは比較的小さく、2 つのstringスライス参照: 2 * 3 * 8 = 64 ビット アーキテクチャ マシンでは 48 バイトです。

単純に次のように書くこともできます:

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

rangeただし、コピーを作成するがスライス境界チェックを最小限に抑える句で同等の結果を取得する方法は次のとおりです。

for i, attr := range n.Attr {
    if attr.Key == "href" {
        n.Attr[i].Val = "something"
    }
}
于 2013-04-11T11:07:12.683 に答える
34

あなたの最後の提案を採用し、範囲のインデックスのみのバージョンを使用します。

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

for one とfor otherを使用するよりも、n.Attr[i]testsKeyの行と sets の行の両方で明示的に参照する方が簡単に思えます。Valattrn.Attr[i]

于 2013-04-15T11:24:28.470 に答える