Чи є петля передбачення в Go?


565

Чи є foreachконструкція мовою Go? Чи можу я повторити фрагмент або масив за допомогою for?



1
Використання rangeв forциклах також згадується в «інтерлюдія про Types» (розділ до його кінця) підручника Go.
kostix

Відповіді:


851

https://golang.org/ref/spec#For_range

Оператор "для" з пунктом "діапазон" повторюється через усі записи масиву, фрагмента, рядка або карти або значень, отриманих на каналі. Для кожного запису він присвоює значення ітерації відповідним змінним ітерації, а потім виконує блок.

Як приклад:

for index, element := range someSlice {
    // index is the index where we are
    // element is the element from someSlice for where we are
}

Якщо вам не важливий індекс, ви можете використовувати _:

for _, element := range someSlice {
    // element is the element from someSlice for where we are
}

Підкреслення,, _- це порожній ідентифікатор , анонімний заповнювач місця.


7
У цьому прикладі elementє значення елемента (копія) - це не сам елемент. Хоча ви можете призначити це element, це не вплине на основну послідовність.
nobar

Я знаю, що в Python і C часто використовується підкреслення як функція для локалізації (тобто gettext ). Чи може використання підкреслення викликати якісь проблеми в Go? Чи навіть Go використовує ту саму бібліотеку для локалізації?
Сергій Колодяжний

2
@SergiyKolodyazhnyy Py Docs говорить «функція (Gettext) зазвичай псевдонімами , як _()в локальному просторі імен» , який тільки за згодою , це не є частиною локалізації Lib. Підкреслення _є дійсною міткою, і це також умовні позначення в Go (і Python, Scala та інших язиках), призначені _для повернутих значень, які ви не будете використовувати. Обсяг _цього прикладу обмежений тілом forпетлі. Якщо у вас є функція, _що охоплює пакет, то вона буде затінена всередині циклу for. Є кілька пакетів для локалізації, я не бачив жодного використання _як імені функції.
Давос

149

Go має foreachсинтаксис -подібний. Він підтримує масиви / фрагменти, карти та канали.

Ітерація через масив або фрагмент :

// index and value
for i, v := range slice {}

// index only
for i := range slice {}

// value only
for _, v := range slice {}

Ітерація над картою :

// key and value
for key, value := range theMap {}

// key only
for key := range theMap {}

// value only
for _, value := range theMap {}

Ітерація через канал :

for v := range theChan {}

Ітерація через канал еквівалентна прийому від каналу до його закриття:

for {
    v, ok := <-theChan
    if !ok {
        break
    }
}

10
Незважаючи на те, що ОП попросив лише використання фрагментів, я вважаю за краще цю відповідь, тому що більшості зрештою знадобляться і інші звичаї.
domoarigato

3
важлива відмінність щодо chanвикористання: перехід через канал витончено вийде з циклу, якщо автор закриє канал у якийсь момент. У for {v := <-theChan} еквіваленті він не буде виходити на канал закритий. Ви можете перевірити це через друге okповернене значення. ПРИКЛАД ТУРУ
colm.anseo

Думав те саме, читаючи його, for { ... }означає нескінченну петлю.
Левіт

13

Наступний приклад показує, як використовувати rangeоператор у forциклі для реалізації foreachциклу.

func PrintXml (out io.Writer, value interface{}) error {
    var data []byte
    var err error

    for _, action := range []func() {
        func () { data, err = xml.MarshalIndent(value, "", "  ") },
        func () { _, err = out.Write([]byte(xml.Header)) },
        func () { _, err = out.Write(data) },
        func () { _, err = out.Write([]byte("\n")) }} {
        action();
        if err != nil {
            return err
        }
    }
    return nil;
}

Приклад повторює масив функцій для уніфікації обробки помилок для функцій. Повний приклад - на дитячому майданчику Google .

PS: це також показує, що підвісні брекети є поганою ідеєю для читабельності коду. Підказка: forумова закінчується безпосередньо перед action()викликом. Очевидно, чи не так?


3
Додайте ,і зрозуміліше, де forзакінчується умова: play.golang.org/p/pcRg6WdxBd - Це фактично перший раз, коли я знайшов протилежний аргумент go fmtстилю, дякую!
topskip

@topskip обидва є дійсними fmt; просто виберіть найкращого :)
Філіп Хаглунд

@FilipHaglund Справа не в тому, якщо вона дійсна. Справа в тому, що в IMO зрозуміліше, де закінчується умова в конкретному випадку вище.
topskip

8
На мою думку, ця відповідь є необґрунтовано складною для цільового питання.
AndreasHassing

@AndreasHassing Як ​​це зробити замість цього, не вводячи надмірності?
закінчення

10

Фактично ви можете використовувати rangeбез посилання на його повернені значення, використовуючи for rangeпроти свого типу:

arr := make([]uint8, 5)
i,j := 0,0
for range arr {
    fmt.Println("Array Loop",i)
    i++
}

for range "bytes" {
    fmt.Println("String Loop",j)
    j++
}

https://play.golang.org/p/XHrHLbJMEd


3
Добре знати, але це не стане в нагоді в більшості випадків
Sridhar

Погодився @Sridhar, це досить ніша.
robstarbuck


4

Так, дальність :

Форма діапазону циклу для циклу повторюється над фрагментом або картою.

У діапазоні відрізка повертаються два значення для кожної ітерації. Перший - індекс, а другий - копія елемента в цьому індексі.

Приклад:

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }

    for i := range pow {
        pow[i] = 1 << uint(i) // == 2**i
    }
    for _, value := range pow {
        fmt.Printf("%d\n", value)
    }
}
  • Ви можете пропустити індекс або значення, призначивши _.
  • Якщо ви хочете лише індекс, опустіть значення цілком.

1

Це може бути очевидним, але ви можете вбудувати масив так:

package main

import (
    "fmt"
)

func main() {
    for _, element := range [3]string{"a", "b", "c"} {
        fmt.Print(element)
    }
}

Виходи:

abc

https://play.golang.org/p/gkKgF3y5nmt

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