2013-04-11 10 views
96

に反復しながらの私は、これらのタイプを持っているとしましょう:変更値golang

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から直接ポインタを取得することはできますか?

明らかに、私は反復のためだけに構造を変更する必要はなく、より冗長なソリューションは解決策ではありません。例えば

+2

JavaScriptで 'Array.prototype.forEach'のようなものが必要ですか? –

+0

それは興味深い考えです。それは解決策でしたが、各反復で関数を呼び出す関数を呼び出すと、サーバー側の言語で重く見えます。ジェネリック医薬品の欠如は、これをさらに重くするでしょう。 –

+0

正直言って、私はそれが重いとは思わない。 1つまたは2つの関数を呼び出すことは非常に安価ですが、これは通常、コンパイラが最も最適化するものです。私はそれを試し、そのベンチマークに合っているかどうかを判断するだろう。 –

答えて

87

いいえ、あなたが望む省略形は不可能です。

これは、反復処理中のスライスの値をrangeがコピーするためです。 specification about 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 

したがって、範囲を効果的に元の値がアンタッチャブル作り、 値がコピーされることを意味するアレイ/スライスについて、その第二の値としてa[i]使用します。

この動作はfollowing codeによって示される:

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によって提案されているように、ポインタまたはインデックスを使用することだけです。

+9

これを考える一つの方法は、値を代入するとコピーが作られるということです。あなたがval:= x [1]を見たならば、valがx [1]のコピーであることは全く驚くことではありません。その範囲を特別なものとして考えるのではなく、範囲の各反復はインデックス変数と値変数を割り当てることから始まり、コピーを引き起こす範囲ではなくその割り当てであることに注意してください。 –

12

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

Playground


出力

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

代替アプローチ:

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

Playground


出力:

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

私はそれが明白だと思ったが、私は取得した構造を変更したくない(彼らは 'go.net/html'パッケージから) –

+1

@dystroy:上記の2番目のアプローチは、構造体 "と呼ぶ)。 – zzzz

+0

はい、私は知っていますが、それは本当に何かをもたらすものではありません。私はおそらく逃したかもしれないという考えを期待していた。私は答えが簡単な解決策はないと確信しています。 –

17

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

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のスライス参照があります:64ビットアーキテクチャマシンで2 * 3 * 8 = 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" 
    } 
} 
+1

'someMap'が' map'の場合、 'value:=&someMap [key]'は動作しません。 – warvariuc

6

私はあなたの最後の提案を採用し、インデックスの範囲限定版を使用します。

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

むしろ1および他のためのn.Attr[i]ためattrを使用するよりも、KeyをテストラインとValを設定する行の両方で明示的にn.Attr[i]を参照するために私には単純に思えます。