Go中有一个foreach循环吗?


Answers:


850

https://golang.org/ref/spec#For_range

带有“ range”子句的“ for”语句遍历数组,切片,字符串或映射的所有条目,或通道上接收到的值。对于每个条目,它将迭代值分配给相应的迭代变量,然后执行该块。

举个例子:

for index, element := range someSlice {
    // index is the index where we are
    // element is the element from someSlice for where we are
}

如果您不关心索引,则可以使用_

for _, element := range someSlice {
    // element is the element from someSlice for where we are
}

下划线_空白标识符,是一个匿名占位符。


7
在这个例子中,element的元件(一个拷贝)的-它不是元素本身。尽管可以分配给element,但这不会影响基础序列。
nobar

我知道在Python和C中经常使用下划线作为本地化功能(即gettext)。下划线的使用会在Go中引起任何问题吗?Go甚至会使用相同的库进行本地化吗?
Sergiy Kolodyazhnyy

2
@SergiyKolodyazhnyy Py docs说:“(gettext)函数通常_()在本地名称空间中使用别名”,这只是按照惯例,它不是本地化库的一部分。下划线_是有效的标签,也是Go(以及Python和Scala和其他lang)中的约定,用于分配_您不使用的返回值。_在此示例中,范围仅限于for循环的主体。如果您有一个包范围的函数,_那么它将在for循环的范围内被遮盖。有一些用于本地化的软件包,我还没有看到任何_用作函数名称的用法。
达沃斯

149

Go具有类似foreach的语法。它支持数组/切片,地图和通道。

遍历数组切片

// index and value
for i, v := range slice {}

// index only
for i := range slice {}

// value only
for _, v := range slice {}

遍历地图

// key and value
for key, value := range theMap {}

// key only
for key := range theMap {}

// value only
for _, value := range theMap {}

遍历一个通道

for v := range theChan {}

在通道上进行迭代等效于从通道接收直到关闭:

for {
    v, ok := <-theChan
    if !ok {
        break
    }
}

10
尽管OP仅要求使用分片,但我更喜欢此答案,因为大多数最终还将需要其他用法。
domoarigato

3
关于chan用法的重要区别:如果作家在某个时候关闭了通道,则在通道上进行范围调整将正常退出循环。在for {v := <-theChan} 相当的,它会不会退出在通道关闭。您可以通过第二个ok返回值对此进行测试。旅游示例
colm.anseo,2017年

阅读时的想法相同,for { ... }代表无限循环。
Levite

13

下面的示例显示如何rangefor循环中使用运算符来实现foreach循环。

func PrintXml (out io.Writer, value interface{}) error {
    var data []byte
    var err error

    for _, action := range []func() {
        func () { data, err = xml.MarshalIndent(value, "", "  ") },
        func () { _, err = out.Write([]byte(xml.Header)) },
        func () { _, err = out.Write(data) },
        func () { _, err = out.Write([]byte("\n")) }} {
        action();
        if err != nil {
            return err
        }
    }
    return nil;
}

该示例遍历函数数组以统一函数的错误处理。一个完整的例子是在Google的游乐场

PS:它还表明,吊括号对代码的可读性不是一个好主意。提示:for条件在action()通话之前结束。很明显,不是吗?


3
添加a ,for条件结束的地方更加清楚:play.golang.org/p/pcRg6WdxBd-实际上,这是我第一次找到与go fmt样式相反的参数,谢谢!
2014年

@topskip都有效。只要选择最好的一个就可以了:)
Filip Haglund

@FilipHaglund这不是有效的问题。关键是,IMO在上述特定情况下for条件结束的地方更加清楚。
topskip

8
我认为,针对目标问题,此答案过于复杂。
AndreasHassing '16

@AndreasHassing怎么做而不引入冗余?
ceving

10

实际上,您可以针对您的类型使用,range而无需引用其返回值for range

arr := make([]uint8, 5)
i,j := 0,0
for range arr {
    fmt.Println("Array Loop",i)
    i++
}

for range "bytes" {
    fmt.Println("String Loop",j)
    j++
}

https://play.golang.org/p/XHrHLbJMEd


3
很高兴知道,但这在大多数情况下不会有用
Sridhar

商定@Sridhar,这是相当利基的。
robstarbuck'9


4

是的,范围

for循环的范围形式在切片或映射上迭代。

在切片上进行测距时,每次迭代都会返回两个值。第一个是索引,第二个是该索引处的元素的副本。

范例:

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }

    for i := range pow {
        pow[i] = 1 << uint(i) // == 2**i
    }
    for _, value := range pow {
        fmt.Printf("%d\n", value)
    }
}
  • 您可以通过指定_来跳过索引或值。
  • 如果只需要索引,请完全删除值。

1

这可能很明显,但是您可以像这样内联数组:

package main

import (
    "fmt"
)

func main() {
    for _, element := range [3]string{"a", "b", "c"} {
        fmt.Print(element)
    }
}

输出:

abc

https://play.golang.org/p/gkKgF3y5nmt

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.