英文:
Change values while iterating
问题
假设我有以下类型:
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
获取指针?
显然,我不想为了迭代而更改结构,更冗长的解决方案也不是解决办法。
英文:
Let's suppose I have these types:
type Attribute struct {
Key, Val string
}
type Node struct {
Attr []Attribute
}
and that I want to iterate on my node's attributes to change them.
I would have loved to be able to do:
for _, attr := range n.Attr {
if attr.Key == "href" {
attr.Val = "something"
}
}
but as attr
isn't a pointer, this wouldn't work and I have to do:
for i, attr := range n.Attr {
if attr.Key == "href" {
n.Attr[i].Val = "something"
}
}
Is there a simpler or faster way? Is it possible to directly get pointers from range
?
Obviously I don't want to change the structures just for the iteration and more verbose solutions are no solutions.
答案1
得分: 204
不,你想要的缩写是不可能的。
原因是range
会复制你正在迭代的切片的值。
关于range
的规范中写道:
>
Range 表达式 第一个值 第二个值(如果第二个变量存在)
数组或切片 a [n]E, *[n]E, or []E 索引 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)
}
该代码会打印出来自range
的值和切片中实际值完全不同的内存位置:
0xf84000f010 vs. 0x7f095ed0bf68
0xf84000f014 vs. 0x7f095ed0bf68
0xf84000f018 vs. 0x7f095ed0bf68
所以你唯一能做的就是使用指针或索引,就像jnml和peterSO已经提出的那样。
英文:
No, the abbreviation you want is not possible.
The reason for this is that range
copies the values from the slice you're iterating over.
The specification about range says:
>
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
So, range uses a[i]
as its second value for arrays/slices, which effectively means that the
value is copied, making the original value untouchable.
This behavior is demonstrated by the 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)
}
The code prints you completely different memory locations for the value from range and the actual
value in the slice:
0xf84000f010 vs. 0x7f095ed0bf68
0xf84000f014 vs. 0x7f095ed0bf68
0xf84000f018 vs. 0x7f095ed0bf68
So the only thing you can do is to either use pointers or the index, as already proposed by jnml and peterSO.
答案2
得分: 49
你似乎在寻求与以下代码等效的内容:
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
类型相对较小,是两个string
切片引用:2 * 3 * 8 = 48字节(在64位架构的机器上)。
你也可以简单地写成:
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"
}
}
英文:
You seem to be asking for something equivalent to this:
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)
}
Output:
{[{key value} {href http://www.google.com}]}
{[{key value} {href something}]}
This avoids creating a--possibly large--copy of type Attribute
values, at the expense of slice bounds checks. In your example, type Attribute
is relatively small, two string
slice references: 2 * 3 * 8 = 48 bytes on a 64-bit architecture machine.
You could also simply write:
for i := 0; i < len(n.Attr); i++ {
if n.Attr[i].Key == "href" {
n.Attr[i].Val = "something"
}
}
But, the way to get an equivalent result with a range
clause, which creates a copy but minimizes slice bounds checks, is:
for i, attr := range n.Attr {
if attr.Key == "href" {
n.Attr[i].Val = "something"
}
}
答案3
得分: 46
我会采纳你上次的建议,并使用只有索引的 range 版本。
for i := range n.Attr {
if n.Attr[i].Key == "href" {
n.Attr[i].Val = "something"
}
}
对我来说,直接在测试 Key
的那一行和设置 Val
的那一行中明确引用 n.Attr[i]
,似乎更简单,而不是在其中一个使用 attr
,另一个使用 n.Attr[i]
。
英文:
I'd adapt your last suggestion and use the index-only version of range.
for i := range n.Attr {
if n.Attr[i].Key == "href" {
n.Attr[i].Val = "something"
}
}
It seems simpler to me to refer to n.Attr[i]
explicitly in both the line that tests Key
and the line that sets Val
, rather than using attr
for one and n.Attr[i]
for the other.
答案4
得分: 15
例如:
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:""}
英文:
For example:
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)
}
}
Output
main.Attribute{Key:"foo", Val:""}
main.Attribute{Key:"href", Val:"something"}
main.Attribute{Key:"bar", Val:""}
Alternative approach:
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)
}
}
Output:
main.Attribute{Key:"foo", Val:""}
main.Attribute{Key:"href", Val:"something"}
main.Attribute{Key:"bar", Val:""}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论