Existe uma maneira de iterar em uma fatia ao contrário no Go?

95

Seria conveniente ser capaz de dizer algo como:

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

Respostas:

139

Não, não há um operador conveniente para adicionar ao alcance um existente. Você terá que fazer uma contagem regressiva normal do loop for:

s := []int{5, 4, 3, 2, 1}
for i := len(s)-1; i >= 0; i-- {
   fmt.Println(s[i])
}
Ulf Holm Nielsen
fonte
A página go efetiva é um exemplo, mas esta é realmente um pouco melhor e declara menos variáveis.
Kevin Cantwell
3
IMO Go precisa desesperadamente de uma construção de alcance descendente. Não ter acarreta muito trabalho extra, como podemos ver .... -
Vetor
24
Eu não diria desesperadamente, seria bom.
Adam Kurkiewicz
46

Você também pode fazer:

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
}

Resultado:

1
2
3
4
5

Também aqui: http://play.golang.org/p/l7Z69TV7Vl

zzzz
fonte
14

Variação com índice

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

Que tal usar adiar:

s := []int{5, 4, 3, 2, 1}
for i, _ := range s {
   defer fmt.Println(s[i])
}
Matt
fonte
8
Votei positivamente apenas pelo fato de que trouxe algum conhecimento novo sobre, defermas acredito que usar isso dentro de um loop para reverso é bastante complicado e deve ser bastante ineficaz em termos de memória.
Alexander Trakhimenok
10
Ele "funciona", mas se o loop não for a última coisa na função, você poderá obter resultados inesperados. Exemplo.
Daniel
5
Isso está sendo usado deferde uma forma para a qual não se destina. Não use isso, pois pode ter efeitos colaterais desagradáveis ​​(execução fora de ordem). Basta usar o forloop na resposta aceita. O objetivo do Go é minimizar esse tipo de hacks inteligentes (não), uma vez que eles tendem a morder sua bunda mais tarde.
RickyA
4
Este é um uso hacky de adiar e deve ser evitado. Se esta for, por exemplo, uma função que alguém pode estender no futuro, pode ter consequências indesejadas.
Amir Keibi de
4
Isso não era muito 'duvidoso' o suficiente, então eu fui em frente e adicionei os canais play.golang.org/p/GodEiv1LlIJ
Xeoncross
4

Pode-se usar um canal para reverter uma lista em uma função sem duplicá-la. Isso torna o código mais agradável no meu 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
fonte
Isso me parece uma solução limpa e agradável de usar. É possível generalizar isso usando o tipo []interface{}? Porque a reversefunção presente só suporta strings.
Atmocreations
Com certeza, basta substituir a string pela interface {} e você está pronto para ir. Só quero enfatizar que uma função com assinatura func reverse(lst []interface{}) chan inyterface{} não aceitará mais uma string [] como entrada. Mesmo que a string possa ser convertida na interface {}, [] string não pode ser convertida na [] interface {}. Infelizmente, a função reversa atual é o tipo de função que precisa ser muito reescrita.
user983716
Obrigado. Essa é a parte feia de ir, eu acho - o que é de alguma forma inevitável. Obrigado!
Atmocreations
Eu implementaria uma pilha em vez disso.
ᐅ devrimbaris
0

Quando preciso extrair elementos de uma fatia e intervalo reverso, 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")
    }
}

Resultado:

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}

Playground: https://play.golang.org/p/gx6fJIfb7fo

Vladimir filin
fonte