Czy istnieje sposób na iterację wycinka w odwrotnej kolejności w Go?

98

Byłoby wygodnie móc powiedzieć coś takiego:

for _, element := reverse range mySlice {
        ...
}
agam
źródło

Odpowiedzi:

145

Nie, nie ma wygodnego operatora, który mógłby dodać jeden do zakresu. Będziesz musiał zrobić normalne odliczanie pętli:

s := []int{5, 4, 3, 2, 1}
for i := len(s)-1; i >= 0; i-- {
   fmt.Println(s[i])
}
Ulf Holm Nielsen
źródło
Skuteczne przejdź strona przykładem, ale ten jest faktycznie nieco ładniejszy i deklaruje mniej zmienne.
Kevin Cantwell
3
IMO Go desperacko potrzebuje konstrukcji zstępującej. Brak tego powoduje dużo dodatkowej pracy, jak widzimy ... -
Vector
24
Nie powiedziałbym rozpaczliwie, byłoby miło.
Adam Kurkiewicz
47

Możesz też:

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
}

Wynik:

1
2
3
4
5

Również tutaj: http://play.golang.org/p/l7Z69TV7Vl

zzzz
źródło
15

Odmiana z indeksem

for k := range s {
        k = len(s) - 1 - k
        // now k starts from the end
    }
Nicky Feller
źródło
6

Co powiesz na użycie odroczenia:

s := []int{5, 4, 3, 2, 1}
for i, _ := range s {
   defer fmt.Println(s[i])
}
Matt
źródło
9
Głosowałem w górę tylko za faktem, że przyniosło to trochę nowej wiedzy, deferale uważam, że użycie tego wewnątrz pętli do odwracania jest dość trudne i powinno być dość nieefektywne pod względem pamięci.
Alexander Trakhimenok
11
To „działa”, ale jeśli pętla nie jest ostatnią rzeczą w funkcji, możesz otrzymać nieoczekiwane rezultaty. Przykład.
Daniel,
6
Używa się deferw sposób, do którego nie jest przeznaczony. Nie używaj tego, ponieważ może to mieć nieprzyjemne skutki uboczne (niesprawne wykonanie). Po prostu użyj forpętli w zaakceptowanej odpowiedzi. Go ma na celu zminimalizowanie tego rodzaju sprytnych (nie) hacków, ponieważ mają tendencję do gryzienia cię później w tyłek.
RickyA
6
Jest to hakerskie użycie odroczenia i należy go unikać. Jeśli jest to na przykład funkcja, którą ktoś mógłby rozszerzyć w przyszłości, może to mieć niezamierzone konsekwencje.
Amir Keibi
6
To nie było wystarczająco „niepewne”, więc poszedłem do przodu i dodałem kanały play.golang.org/p/GodEiv1LlIJ
Xeoncross
4

Można by użyć kanału do odwrócenia listy w funkcji bez jej dublowania. To sprawia, że ​​kod jest ładniejszy w moim sensie.

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
źródło
Dla mnie wygląda to na czyste i przyjemne w użyciu rozwiązanie. Czy można to uogólnić za pomocą typu []interface{}? Ponieważ obecna reversefunkcja obsługuje tylko łańcuchy.
Atmocreations
Na pewno, po prostu zamień ciąg na interfejs {} i gotowe. Chcę tylko podkreślić, że funkcja z podpisem func reverse(lst []interface{}) chan inyterface{} nie będzie już przyjmować ciągu znaków [] jako danych wejściowych. Nawet jeśli string może być rzutowany w interfejsie {}, [] string nie może być rzutowany w [] interface {}. Niestety, obecna funkcja odwrotna jest rodzajem funkcji, która wymaga częstego przepisywania.
user983716
Dziękuję Ci. Myślę, że to brzydka część go - co jest w jakiś sposób nieuniknione. Dziękuję Ci!
Atmocreations
Zaimplementowałbym raczej stos niż to.
ᐅ devrimbaris
0

Kiedy muszę wyodrębnić elementy z wycinka i odwrócić zakres, używam czegoś takiego jak ten kod:

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

Wynik:

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}

Plac zabaw: https://play.golang.org/p/gx6fJIfb7fo

Vladimir Filin
źródło