¿Hay un bucle foreach en Go?

Respuestas:

851

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

Una declaración "for" con una cláusula "range" itera a través de todas las entradas de una matriz, segmento, cadena o mapa, o valores recibidos en un canal. Para cada entrada, asigna valores de iteración a las variables de iteración correspondientes y luego ejecuta el bloque.

Como ejemplo:

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

Si no le importa el índice, puede usar _:

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

El guión bajo, _es el identificador en blanco , un marcador de posición anónimo.

davetron5000
fuente
77
En este ejemplo, elementes el valor del elemento (una copia), no es el elemento en sí. Aunque puede asignar a element, esto no afectará la secuencia subyacente.
nobar
Sé que en Python y C es frecuente usar el guión bajo como una función para la localización (es decir, el gettext ). ¿El uso de subrayado podría causar problemas en Go? ¿Go incluso usa la misma biblioteca para la localización?
Sergiy Kolodyazhnyy
3
@SergiyKolodyazhnyy Py docs dice que "la función (gettext) generalmente tiene un alias como _()en el espacio de nombres local", que es solo por convención , no es parte de la biblioteca de localización. El guión bajo _es una etiqueta válida, y también es una convención en Go (y Python y Scala y otros langs) para asignar _valores de retorno que no usará. El alcance de _en este ejemplo está restringido al cuerpo del forbucle. Si tiene una función de ámbito de paquete _, se sombreará dentro del alcance del bucle for. Hay algunos paquetes para la localización, no he visto ningún uso _como nombre de función.
Davos
150

Go tiene una foreachsintaxis similar. Es compatible con matrices / sectores, mapas y canales.

Iterar sobre matriz o corte :

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

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

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

Iterar sobre un mapa :

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

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

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

Iterar sobre un canal :

for v := range theChan {}

Iterar sobre un canal es equivalente a recibir desde un canal hasta que se cierra:

for {
    v, ok := <-theChan
    if !ok {
        break
    }
}
Moshe Revah
fuente
11
Aunque el OP solo solicitó el uso del segmento, prefiero esta respuesta, porque la mayoría eventualmente necesitará los otros usos también.
domoarigato
3
distinción importante sobre el chanuso: si se extiende sobre un canal, saldrá del ciclo con gracia si el escritor cierra el canal en algún momento. En el for {v := <-theChan} equivalente , será no salir en el canal cerca. Puede probar esto a través del segundo okvalor de retorno. EJEMPLO
colm.anseo
Pensado lo mismo al leerlo, for { ... }significa un bucle infinito.
Levite
13

El siguiente ejemplo muestra cómo usar el rangeoperador en un forbucle para implementar un foreachbucle.

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

El ejemplo itera sobre una matriz de funciones para unificar el manejo de errores para las funciones. Un ejemplo completo es en el patio de juegos de Google .

PD: muestra también que los brackets colgantes son una mala idea para la legibilidad del código. Sugerencia: la forcondición termina justo antes de la action()llamada. Obvio, ¿no es así?

ceving
fuente
3
Agregue ay, está más claro dónde fortermina la condición: play.golang.org/p/pcRg6WdxBd : esta es la primera vez que encuentro un argumento contrario al go fmtestilo, ¡gracias!
topskip
@topskip ambos son válidos; simplemente elige el mejor :)
Filip Haglund
@FilipHaglund No es el punto si es válido. El punto es que IMO es más claro dónde termina la condición for en ese caso particular anterior.
topskip
99
En mi opinión, esta respuesta es irrazonablemente compleja para la pregunta específica.
AndreasHassing
@AndreasHassing ¿Cómo hacerlo sin introducir redundancia?
ceving
10

De hecho, puede usar rangesin hacer referencia a sus valores de retorno al usar for rangecontra su tipo:

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
fuente
3
Es bueno saberlo, pero eso no será útil en la mayoría de los casos
Sridhar
De acuerdo @Sridhar es bastante nicho.
robstarbuck
10

El siguiente es el código de ejemplo de cómo usar foreach en golang

package main

import (
    "fmt"
)

func main() {

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

    for index,element := range arrayOne{

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

    }   

}

Este es un ejemplo en ejecución https://play.golang.org/p/LXptmH4X_0

Nisal Edu
fuente
Muy buena explicación!
Darlan Dieterich
A veces es el ejemplo más simple que es el más útil. ¡Gracias! No tengo nada en contra de las respuestas más esotéricas de los otros comentaristas: ciertamente ilustran las complejidades de la programación Go muy idiomática, hasta el punto de que se vuelven ... ilegibles y difíciles de seguir, pero prefiero su respuesta: va directamente al núcleo con el ejemplo más simple posible (que funciona y es obvio por qué funciona).
Gwyneth Llewelyn
4

Sí, rango :

La forma de rango del bucle for itera sobre un segmento o mapa.

Cuando se extiende sobre un segmento, se devuelven dos valores para cada iteración. El primero es el índice, y el segundo es una copia del elemento en ese índice.

Ejemplo:

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)
    }
}
  • Puede omitir el índice o el valor asignándole a _.
  • Si solo desea el índice, suelte el valor por completo.
Amitesh
fuente
1

Esto puede ser obvio, pero puede alinear la matriz así:

package main

import (
    "fmt"
)

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

salidas:

abc

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

jpihl
fuente