ChatGPT解决这个技术问题 Extra ChatGPT

Is there a foreach loop in Go?

Is there a foreach construct in the Go language? Can I iterate over a slice or array using a for?

The usage of range in for loops is also mentioned in the "An Interlude about Types" section (towards its end) of the Go tutorial.

6
6 revs, 5 users 31%

From For statements with range clause:

A "for" statement with a "range" clause iterates through all entries of an array, slice, string or map, or values received on a channel. For each entry it assigns iteration values to corresponding iteration variables and then executes the block.

As an example:

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

If you don't care about the index, you can use _:

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

The underscore, _, is the blank identifier, an anonymous placeholder.


In this example, element is the value of the element (a copy) -- it is not the element itself. Although you can assign to element, this will not effect the underlying sequence.
I know in Python and C it's frequent to use underscore as a function for localization ( i.e. the gettext ). Would the use of underscore cause any problems in Go ? Does Go even use the same library for localization ?
@SergiyKolodyazhnyy Py docs says "(gettext) function is usually aliased as _() in the local namespace" which is just by convention, it's not part of the localization lib. The underscore _ is a valid label, and it's also convention in Go (and Python and Scala and other langs) to assign to _ for return values you won't use. The scope of _ in this example is restricted to the body of the for loop. If you have a package-scoped function _ then it would be shadowed inside the scope of the for loop. There's a few packages for localization, I've not seen any use _ as a function name.
See Moshe Revah's answer below for more usage examples of for...range. Includes slices, maps and channels.
d
dev.bmax

Go has a foreach-like syntax. It supports arrays/slices, maps and channels.

Iterate over an array or a slice:

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

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

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

Iterate over a map:

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

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

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

Iterate over a channel:

for v := range theChan {}

Iterating over a channel is equivalent to receiving from a channel until it is closed:

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

Although the OP only asked for slice usage, I prefer this answer, because most will eventually need the other usages as well.
important distinction about the chan usage: ranging over a channel will gracefully exit the loop if the writer closes the channel at some point. In the for {v := <-theChan} equivalent, it will not exit on channel close. You can test for this via the second ok return value. TOUR EXAMPLE
Thought the same when reading it, for { ... } stands for an infinite loop.
How about just for without both key, value.I just want to run specific len. And do nothing with array. Ex: for _ := range slice{}
P
Peter Mortensen

Following is the example code for how to use foreach in Go:

package main

import (
    "fmt"
)

func main() {

    arrayOne := [3]string{"Apple", "Mango", "Banana"}

    for index,element := range arrayOne{

        fmt.Println(index)
        fmt.Println(element)

    }

}

This is a running example https://play.golang.org/p/LXptmH4X_0


Sometimes it's the simplest example that is the most useful. Thanks! I've got nothing against the most esoteric answers from the other commenters — they certainly illustrate the intricacies of very idiomatic Go programming, to the point that they become... unreadable and hard to follow — but I prefer your answer: it goes straight to the core with the simplest possible example (which works and it's obvious why it works).
c
ceving

The following example shows how to use the range operator in a for loop to implement a foreach loop.

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

The example iterates over an array of functions to unify the error handling for the functions. A complete example is at Google´s playground.

PS: it shows also that hanging braces are a bad idea for the readability of code. Hint: the for condition ends just before the action() call. Obvious, isn't it?


Add a , and it's clearer where the for condition ends: play.golang.org/p/pcRg6WdxBd - This is actually the first time I've found a counter argument to the go fmt style, thanks!
@topskip both are go fmt valid; just pick the best one :)
@FilipHaglund It is not the point if it is valid. The point is that IMO it's clearer where the for condition ends in that particular case above.
In my opinion, this answer is unreasonably complex for the targeted question.
@AndreasHassing How to do it instead without introducing redundancy?
P
Peter Mortensen

You can in fact use range without referencing its return values by using for range against your type:

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


Good to know but that's not going to be useful in most cases
Agreed @Sridhar it's pretty niche.
P
Peter Mortensen

Yes, range:

The range form of the for loop iterates over a slice or map.

When ranging over a slice, two values are returned for each iteration. The first is the index, and the second is a copy of the element at that index.

Example:

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

You can skip the index or value by assigning to _.

If you only want the index, drop the , value entirely.


j
jpihl

This may be obvious, but you can inline the array like so:

package main

import (
    "fmt"
)

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

outputs:

abc

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


P
Peter Mortensen

I have just implemented this library: https://github.com/jose78/go-collection.

This is an example of how to use the Foreach loop:

package main

import (
    "fmt"

    col "github.com/jose78/go-collection/collections"
)

type user struct {
    name string
    age  int
    id   int
}

func main() {
    newList := col.ListType{user{"Alvaro", 6, 1}, user{"Sofia", 3, 2}}
    newList = append(newList, user{"Mon", 0, 3})

    newList.Foreach(simpleLoop)

    if err := newList.Foreach(simpleLoopWithError); err != nil{
        fmt.Printf("This error >>> %v <<< was produced", err )
    }
}

var simpleLoop col.FnForeachList = func(mapper interface{}, index int) {
    fmt.Printf("%d.- item:%v\n", index, mapper)
}


var simpleLoopWithError col.FnForeachList = func(mapper interface{}, index int) {
    if index > 1{
        panic(fmt.Sprintf("Error produced with index == %d\n", index))
    }
    fmt.Printf("%d.- item:%v\n", index, mapper)
}

The result of this execution should be:

0.- item:{Alvaro 6 1}
1.- item:{Sofia 3 2}
2.- item:{Mon 0 3}
0.- item:{Alvaro 6 1}
1.- item:{Sofia 3 2}
Recovered in f Error produced with index == 2

ERROR: Error produced with index == 2
This error >>> Error produced with index == 2
 <<< was produced

Try this code in playGrounD.


How is it a for-each loop? Isn't it just list processing? Can you elaborate in your answer? (But without "Edit:", "Update:", or similar - the question/answer should appear as if it was written today.)