Чи є спосіб перебирати фрагмент у зворотному напрямку в Go?


98

Було б зручно мати можливість сказати щось на зразок:

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

Відповіді:


145

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

s := []int{5, 4, 3, 2, 1}
for i := len(s)-1; i >= 0; i-- {
   fmt.Println(s[i])
}

Приклад ефективного переходу на сторінку, але цей насправді трохи приємніший і оголошує менше змінних.
Kevin Cantwell

3
IMO Go відчайдушно потребує конструкції спадного діапазону. Як ми бачимо, його відсутність спричиняє багато додаткової роботи .... -
Вектор

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



6

Як щодо використання відстрочки:

s := []int{5, 4, 3, 2, 1}
for i, _ := range s {
   defer fmt.Println(s[i])
}

9
Я проголосував лише за те, що це принесло деякі нові знання, deferале я вважаю, що використовувати це всередині циклу для зворотного досить складно і повинно бути досить неефективним з точки зору пам'яті.
Олександр Трахименок

11
Він "працює", але якщо цикл не є останньою функцією функції, ви можете отримати несподівані результати. Приклад.
Даніель

6
Це використовується deferтаким чином, що воно не призначене. Не використовуйте це, оскільки це може мати неприємні побічні ефекти (поза виконанням замовлення). Просто використовуйте forцикл у прийнятій відповіді. Go прагне мінімізувати такий розумний (не) хак, оскільки пізніше вони, як правило, кусають вас за дупу.
RickyA,

6
Це хакерське використання відстрочки, якого слід уникати. Якщо це, наприклад, функція, яку хтось може розширити в майбутньому, це може мати непередбачені наслідки.
Амір Кейбі

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

Це здається мені чистим та приємним у використанні рішенням. Чи можна узагальнити це за допомогою типу []interface{}? Оскільки поточна reverseфункція підтримує лише рядки.
Атомації

Звичайно, просто замініть рядок інтерфейсом {}, і ви готові до роботи. Я просто хочу підкреслити, що функція з підписом func reverse(lst []interface{}) chan inyterface{} більше не буде приймати рядок [] як вхід. Навіть якщо рядок може бути залитий в інтерфейсі {}, [] рядок не може бути залитий в [] інтерфейсі {}. На жаль, справжня функція зворотного зв'язку - це та функція, яку потрібно багато переписувати.
user983716

Дякую. Думаю, це потворна частина ходу - чогось якось не уникнути. Дякую!
Атомації

Я б реалізував стек, а не цей.
vr 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.