[arrays] 반복하는 동안 값 변경

이 유형이 있다고 가정 해 봅시다.

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있습니까?

분명히 반복을 위해 구조를 변경하고 싶지 않으며 더 자세한 솔루션은 솔루션이 아닙니다.



답변

아니요, 원하는 약어는 불가능합니다.

그 이유 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]배열 / 슬라이스의 두 번째 값으로 사용 됩니다. 즉, 값이 복사되어 원래 값을 건드리지 못하게됩니다.

이 동작은 다음 코드로 설명됩니다 .

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에서 이미 제안한 것처럼 포인터 또는 색인을 사용하는 것이 유일한 방법입니다.


답변

당신은 이것과 동등한 것을 요구하는 것 같습니다 :

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슬라이스 경계 검사를 희생시키면서 유형 값을 복사 할 수 있습니다 . 귀하의 예에서 type Attributestring64 비트 아키텍처 시스템에서 비교적 작은 두 개의 슬라이스 참조입니다 (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"
    }
}


답변

마지막 제안을 수정하고 색인 전용 버전의 범위를 사용하고 싶습니다.

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

n.Attr[i]테스트 Key하는 줄과 설정하는 줄 모두 를 명시 적으로 언급하는 Val것이 attr하나와 n.Attr[i]다른 것을 사용 하는 것보다 간단합니다 .


답변

예를 들면 다음과 같습니다.

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

운동장


산출

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

운동장


산출:

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


답변