【问题标题】:Go how to properly use the for ... range loopGo 如何正确使用 for ... range 循环
【发布时间】:2016-04-10 18:20:04
【问题描述】:

目前我有一个包含以下代码的 go 程序。

package main

import "time"
import "minions/minion"

func main() {
    // creating the slice
    ms := make([]*minion.Minion, 2)

    //populating the slice and make the elements start doing something
    for i := range ms  {
        m := &ms[i]
        *m = minion.NewMinion()
        (*m).Start()
    }

    // wait while the minions do all the work
    time.Sleep(time.Millisecond * 500)

    // make the elements of the slice stop with what they were doing
    for i := range ms {
        m := &ms[i]
        (*m).Stop()
    }
}

这里NewMinion()是一个构造函数,它返回一个*minion.Minion

代码运行良好,但每次我使用for ... range 循环时都必须编写m := &ms[i],在我看来,应该有更友好的代码编写方式来解决这个问题。

理想情况下,我希望以下内容成为可能(使用组成的 &range 标签):

package main

import "time"
import "minions/minion"

func main() {
    // creating the slice
    ms := make([]*minion.Minion, 2)

    //populating the slice and make the elements start doing something
    for _, m := &range ms  {
        *m = minion.NewMinion()
        (*m).Start()
    }

    // wait while the minions do all the work
    time.Sleep(time.Millisecond * 500)

    // make the elements of the slice stop with what they were doing
    for _, m := &range ms {
        (*m).Stop()
    }
}

很遗憾,这还不是语言功能。关于从代码中删除m := &ms[i] 的最佳方法是什么?还是没有比这更省力的写法了?

【问题讨论】:

    标签: for-loop go range slice


    【解决方案1】:

    您的第一个示例是一个指针切片,您不需要每次都获取切片中指针的地址然后取消引用指针。更惯用的 Go 看起来像(稍微编辑以在没有“minion”包的情况下在操场上运行):

    http://play.golang.org/p/88WsCVonaL

    // creating the slice
    ms := make([]*Minion, 2)
    
    //populating the slice and make the elements start doing something
    for i := range ms {
        ms[i] = NewMinion(i)
        ms[i].Start()
    
        // (or equivalently) 
        // m := MewMinion(i)
        // m.Start()
        // ms[i] = m
    }
    
    // wait while the minions do all the work
    time.Sleep(time.Millisecond * 500)
    
    // make the elements of the slice stop with what they were doing
    for _, m := range ms {
        m.Stop()
    }
    

    【讨论】:

    • 我确实想过,但是当您使用 ms[i] 超过 2 次时,我认为直接访问该结构会更好,而不是通过所有切片访问它时间,因为:A. 你必须一直写括号,B. 在大多数语言中,访问数组中的元素需要(一点点)处理时间 afaik,因此我只想做一次。所以从某种意义上说,我正在尝试优化代码编写时间和处理时间。
    • @coolcat007:你的优化过早了。如果取消引用指针和内存局部性对您的程序产生可衡量的影响,我认为您可以处理额外索引操作的轻微不便。否则,使用带有指针的切片并让您的代码正常工作。如果您想在没有索引的情况下使用“minion”做更多事情,只需将其分配给一个变量并在完成后将其粘贴到切片中:m := NewMinion(i); ...; ms[i] = m
    • 感谢您的帮助。在我看来,最后一个建议是解决这个问题的最佳方法。它还有助于防止不必要地使用指针。
    • @coolcat007:我认为您可能对这里的指针有一些误解,因为我的建议根本不会改变指针的使用,并且在语义上是等效的。 Go 中很少需要手动取消引用;也许Selectors 上规范的相关部分可以解决问题。
    • 不,我的意思是您只在ms[i] = m 中取消引用指针一次,而不是在实例化结构和调用函数时多次取消引用它。但是,在我的第一条评论中,我忘记了访问数组中的元素基本上也是取消引用。还是我在最后一点上错了?
    【解决方案2】:

    这都是错误的。

    绝对不需要在代码中获取指针的地址。 ms 是一个指针切片,你的构造函数返回一个指针,所以直接分配 i :

    for i := range ms  {
        ms[i] = minion.NewMinion()
        ms[i].Start()
    }
    

    很简单。

    【讨论】:

    • 我想你的意思是 ms[i],而不是 m[i]?因为 m 不是切片
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多