Gibt es eine foreach-Schleife in Go?

565

Gibt es ein foreachKonstrukt in der Go-Sprache? Kann ich mit a über ein Slice oder Array iterieren for?

Tatsuhirosatou
quelle
1
Die Verwendung von rangeIn- forSchleifen wird auch im Abschnitt "Ein Zwischenspiel über Typen" (gegen Ende) des Go-Tutorials erwähnt.
Kostix

Antworten:

851

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

Eine "for" -Anweisung mit einer "range" -Klausel durchläuft alle Einträge eines Arrays, Slice, Strings oder Map oder Werte, die auf einem Kanal empfangen wurden. Für jeden Eintrag werden entsprechende Iterationsvariablen Iterationswerte zugewiesen und anschließend der Block ausgeführt.

Als Beispiel:

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

Wenn Sie sich nicht für den Index interessieren, können Sie Folgendes verwenden _:

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

Der Unterstrich _ist die leere Kennung , ein anonymer Platzhalter.

davetron5000
quelle
7
In diesem Beispiel elementist dies der Wert des Elements (eine Kopie) - es ist nicht das Element selbst. Obwohl Sie zuweisen können, elementhat dies keine Auswirkungen auf die zugrunde liegende Sequenz.
Nobar
Ich weiß, dass in Python und C häufig Unterstriche als Funktion für die Lokalisierung verwendet werden (dh der gettext ). Würde die Verwendung von Unterstrichen Probleme in Go verursachen? Verwendet Go überhaupt dieselbe Bibliothek für die Lokalisierung?
Sergiy Kolodyazhnyy
2
@SergiyKolodyazhnyy Py docs sagt, dass "(gettext) -Funktion normalerweise wie _()im lokalen Namespace aliasiert ist ", was nur konventionell ist und nicht Teil der Lokalisierungsbibliothek ist. Der Unterstrich _ist eine gültige Bezeichnung, und es ist auch eine Konvention in Go (und Python und Scala und anderen langs), die _für Rückgabewerte zugewiesen wird, die Sie nicht verwenden. Der Umfang von _in diesem Beispiel ist auf den Hauptteil der forSchleife beschränkt. Wenn Sie eine Funktion mit Paketbereich haben, _wird diese im Bereich der for-Schleife schattiert. Es gibt ein paar Pakete für die Lokalisierung, ich habe keine Verwendung _als Funktionsname gesehen.
Davos
149

Go hat eine foreachähnliche Syntax. Es unterstützt Arrays / Slices, Maps und Kanäle.

Iterieren Sie über Array oder Slice :

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

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

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

Über eine Karte iterieren :

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

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

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

Über einen Kanal iterieren :

for v := range theChan {}

Das Iterieren über einen Kanal entspricht dem Empfangen von einem Kanal, bis dieser geschlossen wird:

for {
    v, ok := <-theChan
    if !ok {
        break
    }
}
Moshe Revah
quelle
10
Obwohl das OP nur nach der Verwendung von Slices gefragt hat, bevorzuge ich diese Antwort, da die meisten letztendlich auch die anderen Verwendungen benötigen werden.
Domoarigato
3
Wichtiger Unterschied zur chanVerwendung: Wenn Sie sich über einen Kanal erstrecken, wird die Schleife ordnungsgemäß verlassen, wenn der Schreiber den Kanal irgendwann schließt. In der for {v := <-theChan} äquivalenten , wird es nicht auf Kanal Schließen beenden. Sie können dies über den zweiten okRückgabewert testen . TOUR
BEISPIEL
Beim Lesen gleich gedacht, for { ... }steht für eine Endlosschleife.
Levite
13

Das folgende Beispiel zeigt, wie der rangeOperator in einer forSchleife zum Implementieren einer foreachSchleife verwendet wird.

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

Das Beispiel durchläuft ein Array von Funktionen, um die Fehlerbehandlung für die Funktionen zu vereinheitlichen. Ein vollständiges Beispiel finden Sie auf dem Google- Spielplatz .

PS: Es zeigt auch, dass hängende Klammern eine schlechte Idee für die Lesbarkeit von Code sind. Hinweis: Die forBedingung endet kurz vor dem action()Anruf. Offensichtlich, nicht wahr?

ceving
quelle
3
Fügen Sie ein hinzu ,und es ist klarer, wo die forBedingung endet: play.golang.org/p/pcRg6WdxBd - Dies ist tatsächlich das erste Mal, dass ich ein Gegenargument zum go fmtStil gefunden habe, danke!
Topskip
@topskip beide sind gültig; wähle einfach die beste aus :)
Filip Haglund
@FilipHaglund Es ist nicht der Punkt, ob es gültig ist. Der Punkt ist, dass IMO es klarer ist, wo die for-Bedingung in diesem speziellen Fall oben endet.
Topskip
8
Meiner Meinung nach ist diese Antwort für die angesprochene Frage unangemessen komplex.
AndreasHassing
@AndreasHassing Wie geht das stattdessen, ohne Redundanz einzuführen?
16.
10

Sie können tatsächlich verwenden, rangeohne auf die Rückgabewerte zu verweisen, indem Sie Folgendes for rangegegen Ihren Typ verwenden:

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

Robstarbuck
quelle
3
Gut zu wissen, aber das wird in den meisten Fällen nicht nützlich sein
Sridhar
Einverstanden @Sridhar es ist ziemlich Nische.
Robstarbuck
9

Im Folgenden finden Sie den Beispielcode für die Verwendung von foreach in Golang

package main

import (
    "fmt"
)

func main() {

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

    for index,element := range arrayOne{

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

    }   

}

Dies ist ein laufendes Beispiel https://play.golang.org/p/LXptmH4X_0

Nisal Edu
quelle
sehr gute Erklärung!
Darlan Dieterich
4

Ja, Reichweite :

Die Bereichsform der for-Schleife iteriert über ein Slice oder eine Map.

Bei einem Bereich über ein Slice werden für jede Iteration zwei Werte zurückgegeben. Der erste ist der Index und der zweite ist eine Kopie des Elements an diesem Index.

Beispiel:

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)
    }
}
  • Sie können den Index oder Wert überspringen, indem Sie _ zuweisen.
  • Wenn Sie nur den Index möchten, löschen Sie den Wert vollständig.
Amitesh
quelle
1

Dies mag offensichtlich sein, aber Sie können das Array wie folgt einbinden:

package main

import (
    "fmt"
)

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

Ausgänge:

abc

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

jpihl
quelle