Есть ли способ перебрать срез в обратном направлении в Go?

98

Было бы удобно сказать что-то вроде:

for _, element := reverse range mySlice {
        ...
}
агам
источник

Ответы:

146

Нет, нет удобного оператора, чтобы добавить к диапазону один на месте. Вам нужно будет выполнить обычный обратный отсчет цикла:

s := []int{5, 4, 3, 2, 1}
for i := len(s)-1; i >= 0; i-- {
   fmt.Println(s[i])
}
Ульф Хольм Нильсен
источник
Эффективная идут страница примера, но это один на самом деле немного лучше , и объявляет меньше переменных.
Кевин Кэнтуэлл
3
IMO Go отчаянно нуждается в конструкции нисходящего диапазона. Как мы видим, отсутствие этого требует много дополнительной работы .... -
Vector
24
Я бы не сказал в отчаянии, это было бы неплохо.
Адам Куркевич
47

Вы также можете:

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
}

Выход:

1
2
3
4
5

Также здесь: http://play.golang.org/p/l7Z69TV7Vl

zzzz
источник
15

Вариация с индексом

for k := range s {
        k = len(s) - 1 - k
        // now k starts from the end
    }
Ники Феллер
источник
6

Как насчет использования отсрочки:

s := []int{5, 4, 3, 2, 1}
for i, _ := range s {
   defer fmt.Println(s[i])
}
Мэтт
источник
9
Я проголосовал за то, что это принесло некоторые новые знания, deferно я считаю, что использовать это внутри цикла для реверса довольно сложно и должно быть довольно неэффективным с точки зрения памяти.
Александр Трахименок
11
Это «работает», но если цикл - не последнее, что есть в функции, вы можете получить неожиданные результаты. Пример.
Daniel
6
Это deferне предназначено для использования. Не используйте это, так как это может иметь неприятные побочные эффекты (выполнение вне очереди). Просто используйте forцикл в принятом ответе. Go стремится свести к минимуму такие хитрые (не) хаки, поскольку они, как правило, в дальнейшем укусят вас под зад.
RickyA
6
Это хакерское использование defer, которого следует избегать. Если это, например, функция, которую кто-то может расширить в будущем, это может иметь непредвиденные последствия.
Амир Кейби
6
Этого было недостаточно, поэтому я пошел дальше и добавил каналы play.golang.org/p/GodEiv1LlIJ
Xeoncross
4

Можно использовать канал для переворота списка в функции без его дублирования. В моем понимании это делает код лучше.

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
источник
Мне это кажется чистым и удобным в использовании решением. Можно ли это обобщить по типу []interface{}? Потому что reverseфункция present поддерживает только строки.
Atmocreations
Конечно, просто замените строку на interface {}, и все готово. Я просто хочу подчеркнуть, что функция с подписью func reverse(lst []interface{}) chan inyterface{} больше не будет принимать строку [] в качестве входных данных. Даже если строка может быть преобразована в интерфейс {}, строка [] не может быть преобразована в [] интерфейс {}. К сожалению, настоящая обратная функция - это такая функция, которую нужно много переписывать.
user983716 06
Спасибо. Я думаю, что это уродливая часть гоу, которая почему-то неизбежна. Спасибо!
Atmocreations
Я бы реализовал стек, а не это.
ᐅ devrimbaris
0

Когда мне нужно извлечь элементы из среза и обратного диапазона, я использую что-то вроде этого кода:

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

Выход:

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}

Площадка: https://play.golang.org/p/gx6fJIfb7fo

Владимир Филин
источник