¿Hay alguna forma de iterar sobre un segmento en reversa en Go?

98

Sería conveniente poder decir algo como:

for _, element := reverse range mySlice {
        ...
}
agam
fuente

Respuestas:

145

No, no hay un operador conveniente para agregarlo al rango existente. Tendrá que hacer una cuenta regresiva normal de bucle for:

s := []int{5, 4, 3, 2, 1}
for i := len(s)-1; i >= 0; i-- {
   fmt.Println(s[i])
}
Ulf Holm Nielsen
fuente
La página go efectiva es un ejemplo, pero esta en realidad es un poco mejor y declara menos variables.
Kevin Cantwell
3
IMO Go necesita desesperadamente una construcción de rango descendente. No tenerlo provoca mucho trabajo extra, como podemos ver .... -
Vector
24
No diría desesperadamente, sería bueno tenerlo.
Adam Kurkiewicz
47

También puedes hacer:

s := []int{5, 4, 3, 2, 1}
for i := range s {
        fmt.Println(s[len(s)-1-i]) // Suggestion: do `last := len(s)-1` before the loop
}

Salida:

1
2
3
4
5

También aquí: http://play.golang.org/p/l7Z69TV7Vl

zzzz
fuente
15

Variación con índice

for k := range s {
        k = len(s) - 1 - k
        // now k starts from the end
    }
Nicky Feller
fuente
6

¿Qué tal el uso diferido?

s := []int{5, 4, 3, 2, 1}
for i, _ := range s {
   defer fmt.Println(s[i])
}
Mate
fuente
9
He votado solo por el hecho de que trajo algunos conocimientos nuevos, deferpero creo que usar esto dentro de un bucle para revertir es bastante complicado y debería ser bastante ineficaz en cuanto a la memoria.
Alexander Trakhimenok
11
"Funciona", pero si el bucle no es lo último en la función, puede obtener resultados inesperados. Ejemplo.
Daniel
6
Esto se usa deferde una manera para la que no está diseñado. No use esto ya que puede tener efectos secundarios desagradables (ejecución fuera de orden). Simplemente use el forbucle en la respuesta aceptada. Go tiene como objetivo minimizar este tipo de (no) trucos inteligentes , ya que tienden a morderte el culo más adelante.
RickyA
6
Este es un uso engañoso de aplazar y debe evitarse. Si esta es, por ejemplo, una función que alguien podría extender en el futuro, puede tener consecuencias no deseadas.
Amir Keibi
6
Esto no fue lo suficientemente 'dudoso', así que seguí adelante y agregué canales play.golang.org/p/GodEiv1LlIJ
Xeoncross
4

Se podría usar un canal para invertir una lista en una función sin duplicarla. Hace que el código sea más agradable en mi sentido.

package main

import (
    "fmt"
)

func reverse(lst []string) chan string {
    ret := make(chan string)
    go func() {
        for i, _ := range lst {
            ret <- lst[len(lst)-1-i]
        }
        close(ret)
    }()
    return ret
}

func main() {
    elms := []string{"a", "b", "c", "d"}
    for e := range reverse(elms) {
        fmt.Println(e)
    }
}
user983716
fuente
Me parece una solución limpia y agradable de usar. ¿Es posible generalizar esto usando el tipo []interface{}? Porque la función presente reversesolo admite cadenas.
Atmocreations
Por supuesto, simplemente reemplace la cadena por la interfaz {} y estará listo para comenzar. Solo quiero enfatizar que una función con firma func reverse(lst []interface{}) chan inyterface{} ya no tomará una cadena [] como entrada. Incluso si la cadena se puede convertir en la interfaz {}, [] la cadena no se puede convertir en la [] interfaz {}. Desafortunadamente, la función inversa actual es el tipo de función que debe reescribirse mucho.
user983716
Gracias. Creo que esa es la parte fea de ir, que de alguna manera es inevitable. ¡Gracias!
Atmocreations
Implementaría una pila en lugar de esto.
ᐅ devrimbaris
0

Cuando necesito extraer elementos de un segmento y rango inverso, uso algo como este código:

// reverse range
// Go Playground: https://play.golang.org/p/gx6fJIfb7fo
package main

import (
    "fmt"
)

type Elem struct {
    Id   int64
    Name string
}

type Elems []Elem

func main() {
    mySlice := Elems{{Id: 0, Name: "Alice"}, {Id: 1, Name: "Bob"}, {Id: 2, Name: "Carol"}}
    for i, element := range mySlice {
        fmt.Printf("Normal  range: [%v] %+v\n", i, element)
    }

    //mySlice = Elems{}
    //mySlice = Elems{{Id: 0, Name: "Alice"}}
    if last := len(mySlice) - 1; last >= 0 {
        for i, element := last, mySlice[0]; i >= 0; i-- {
            element = mySlice[i]
            fmt.Printf("Reverse range: [%v] %+v\n", i, element)
        }
    } else {
        fmt.Println("mySlice empty")
    }
}

Salida:

Normal  range: [0] {Id:0 Name:Alice}
Normal  range: [1] {Id:1 Name:Bob}
Normal  range: [2] {Id:2 Name:Carol}
Reverse range: [2] {Id:2 Name:Carol}
Reverse range: [1] {Id:1 Name:Bob}
Reverse range: [0] {Id:0 Name:Alice}

Zona de juegos: https://play.golang.org/p/gx6fJIfb7fo

Vladimir Filin
fuente