Есть ли способ перебора целого ряда целых чисел в Голанге?

Диапазон Golang может перебирать карты и срезы, но мне было интересно, есть ли способ перебирать диапазон чисел, что-то вроде этого

for i := range [1..10] {
    fmt.Println(i)
}

или есть способ представить диапазон целых чисел в Go, как это делает ruby?

Ответ 1

Вы можете и должны просто написать цикл for. Простой, очевидный код - это путь Go.

for i := 1; i <= 10; i++ {
    fmt.Println(i)
}

Ответ 2

Вот программа для сравнения двух способов, предложенных до сих пор

import (
    "fmt"

    "github.com/bradfitz/iter"
)

func p(i int) {
    fmt.Println(i)
}

func plain() {
    for i := 0; i < 10; i++ {
        p(i)
    }
}

func with_iter() {
    for i := range iter.N(10) {
        p(i)
    }
}

func main() {
    plain()
    with_iter()
}

Скомпилируйте, чтобы создать разборку

go build -gcflags -S iter.go

Вот простой (я удалил из инструкций исключение)

Настройка

0035 (/home/ncw/Go/iter.go:14) MOVQ    $0,AX
0036 (/home/ncw/Go/iter.go:14) JMP     ,38

петли

0037 (/home/ncw/Go/iter.go:14) INCQ    ,AX
0038 (/home/ncw/Go/iter.go:14) CMPQ    AX,$10
0039 (/home/ncw/Go/iter.go:14) JGE     $0,45
0040 (/home/ncw/Go/iter.go:15) MOVQ    AX,i+-8(SP)
0041 (/home/ncw/Go/iter.go:15) MOVQ    AX,(SP)
0042 (/home/ncw/Go/iter.go:15) CALL    ,p+0(SB)
0043 (/home/ncw/Go/iter.go:15) MOVQ    i+-8(SP),AX
0044 (/home/ncw/Go/iter.go:14) JMP     ,37
0045 (/home/ncw/Go/iter.go:17) RET     ,

И вот с_имя

Настройка

0052 (/home/ncw/Go/iter.go:20) MOVQ    $10,AX
0053 (/home/ncw/Go/iter.go:20) MOVQ    $0,~r0+-24(SP)
0054 (/home/ncw/Go/iter.go:20) MOVQ    $0,~r0+-16(SP)
0055 (/home/ncw/Go/iter.go:20) MOVQ    $0,~r0+-8(SP)
0056 (/home/ncw/Go/iter.go:20) MOVQ    $type.[]struct {}+0(SB),(SP)
0057 (/home/ncw/Go/iter.go:20) MOVQ    AX,8(SP)
0058 (/home/ncw/Go/iter.go:20) MOVQ    AX,16(SP)
0059 (/home/ncw/Go/iter.go:20) PCDATA  $0,$48
0060 (/home/ncw/Go/iter.go:20) CALL    ,runtime.makeslice+0(SB)
0061 (/home/ncw/Go/iter.go:20) PCDATA  $0,$-1
0062 (/home/ncw/Go/iter.go:20) MOVQ    24(SP),DX
0063 (/home/ncw/Go/iter.go:20) MOVQ    32(SP),CX
0064 (/home/ncw/Go/iter.go:20) MOVQ    40(SP),AX
0065 (/home/ncw/Go/iter.go:20) MOVQ    DX,~r0+-24(SP)
0066 (/home/ncw/Go/iter.go:20) MOVQ    CX,~r0+-16(SP)
0067 (/home/ncw/Go/iter.go:20) MOVQ    AX,~r0+-8(SP)
0068 (/home/ncw/Go/iter.go:20) MOVQ    $0,AX
0069 (/home/ncw/Go/iter.go:20) LEAQ    ~r0+-24(SP),BX
0070 (/home/ncw/Go/iter.go:20) MOVQ    8(BX),BP
0071 (/home/ncw/Go/iter.go:20) MOVQ    BP,autotmp_0006+-32(SP)
0072 (/home/ncw/Go/iter.go:20) JMP     ,74

петли

0073 (/home/ncw/Go/iter.go:20) INCQ    ,AX
0074 (/home/ncw/Go/iter.go:20) MOVQ    autotmp_0006+-32(SP),BP
0075 (/home/ncw/Go/iter.go:20) CMPQ    AX,BP
0076 (/home/ncw/Go/iter.go:20) JGE     $0,82
0077 (/home/ncw/Go/iter.go:20) MOVQ    AX,autotmp_0005+-40(SP)
0078 (/home/ncw/Go/iter.go:21) MOVQ    AX,(SP)
0079 (/home/ncw/Go/iter.go:21) CALL    ,p+0(SB)
0080 (/home/ncw/Go/iter.go:21) MOVQ    autotmp_0005+-40(SP),AX
0081 (/home/ncw/Go/iter.go:20) JMP     ,73
0082 (/home/ncw/Go/iter.go:23) RET     ,

Итак, вы можете видеть, что решение iter значительно дороже, хотя оно полностью включено в фазу установки. В фазе цикла в цикле есть дополнительная инструкция, но это не так уж плохо.

Я бы использовал простой цикл.

Ответ 3

Марку Мишину было предложено использовать срез, но нет причин создавать массив с make и использовать в for возвращенном его фрагменте, когда массив, созданный через литерал, может использоваться и короче

for i := range [5]int{} {
        fmt.Println(i)
}

Ответ 4

iter - очень маленький пакет, который просто предоставляет синтаксически отличный способ перебора целых чисел.

for i := range iter.N(4) {
    fmt.Println(i)
}

Роб Пайк (автор Go) критиковал его:

Кажется, что почти каждый раз, когда кто-то придумывает способ избежать делая что-то вроде цикла for идиоматическим способом, потому что он чувствует слишком долго или громоздко, результат почти всегда больше нажатий клавиш чем то, что предположительно короче. [...] Что оставляет в стороне все сумасшедшие накладные расходы, эти "улучшения" приносят.

Ответ 5

Здесь приведен пример сравнения инструкции Go for с инструкцией ForClause и Go range с использованием пакета iter.

iter_test.go

package main

import (
    "testing"

    "github.com/bradfitz/iter"
)

const loops = 1e6

func BenchmarkForClause(b *testing.B) {
    b.ReportAllocs()
    j := 0
    for i := 0; i < b.N; i++ {
        for j = 0; j < loops; j++ {
            j = j
        }
    }
    _ = j
}

func BenchmarkRangeIter(b *testing.B) {
    b.ReportAllocs()
    j := 0
    for i := 0; i < b.N; i++ {
        for j = range iter.N(loops) {
            j = j
        }
    }
    _ = j
}

// It does not cause any allocations.
func N(n int) []struct{} {
    return make([]struct{}, n)
}

func BenchmarkIterAllocs(b *testing.B) {
    b.ReportAllocs()
    var n []struct{}
    for i := 0; i < b.N; i++ {
        n = iter.N(loops)
    }
    _ = n
}

Вывод:

$ go test -bench=. -run=.
testing: warning: no tests to run
PASS
BenchmarkForClause      2000       1260356 ns/op           0 B/op          0 allocs/op
BenchmarkRangeIter      2000       1257312 ns/op           0 B/op          0 allocs/op
BenchmarkIterAllocs 20000000            82.2 ns/op         0 B/op          0 allocs/op
ok      so/test 7.026s
$

Ответ 6

В то время как я сочувствую вашей обеспокоенности отсутствием этой языковой функции, вы, вероятно, просто захотите использовать обычный цикл for. И вы, вероятно, будете в порядке с этим, чем вы думаете, когда пишете больше кода Go.

Я написал этот пакет iter - который поддерживается простым, идиоматическим циклом for, который возвращает значения над chan int - в попытке улучшить дизайн, найденный в https://github.com/bradfitz/iter, в котором указывалось, что проблемы кэширования и производительности, а также умная, но странная и неинтуитивная реализация. Моя собственная версия работает одинаково:

package main

import (
    "fmt"
    "github.com/drgrib/iter"
)

func main() {
    for i := range iter.N(10) {
        fmt.Println(i)
    }
}

Однако, сравнительный анализ показал, что использование канала было очень дорогостоящим вариантом. Сравнение трех методов, которые можно запустить из iter_test.go в моем пакете с помощью

go test -bench=. -run=.

определяет, насколько плоха его производительность

BenchmarkForMany-4                   5000       329956 ns/op           0 B/op          0 allocs/op
BenchmarkDrgribIterMany-4               5    229904527 ns/op         195 B/op          1 allocs/op
BenchmarkBradfitzIterMany-4          5000       337952 ns/op           0 B/op          0 allocs/op

BenchmarkFor10-4                500000000         3.27 ns/op           0 B/op          0 allocs/op
BenchmarkDrgribIter10-4            500000      2907 ns/op             96 B/op          1 allocs/op
BenchmarkBradfitzIter10-4       100000000        12.1 ns/op            0 B/op          0 allocs/op

В этом процессе этот тест также показывает, как решение bradfitz работает хуже по сравнению со встроенным предложением for для размера цикла 10.

Короче говоря, до сих пор не удалось обнаружить дублирование производительности встроенного предложения for, предоставляя простой синтаксис для [0,n), как тот, который найден в Python и Ruby.

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

for i := range 10 {
    fmt.Println(i)
}

на тот же машинный код, что и for i := 0; i < 10; i++.

Однако, если быть честным, после написания моего собственного iter.N (но до его бенчмаркинга), я вернулся через недавно написанную программу, чтобы увидеть все места, где я мог бы ее использовать. На самом деле их было мало. В некритичном разделе моего кода было только одно место, где я мог бы обойтись без более полного предложения по умолчанию for.

Так что, хотя может показаться, что это огромное разочарование для языка в принципе, вы можете найти - как и я, - что на самом деле вам это действительно не нужно. Как известно, Роб Пайк говорит о дженериках, вы, возможно, не пропустите эту функцию так сильно, как вы думаете.

Ответ 7

Существует уродливое решение, но оно немного ближе к Ruby for i in 0..N или Python for i in range(N)

for i := range make([]int, 5){
    fmt.Println(i)
}

Итак, да, вы должны использовать цикл for i:=0; i<N; i++ {} или iter.

Ответ 8

package main

import "fmt"

func main() {

    nums := []int{2, 3, 4}
    for _, num := range nums {
       fmt.Println(num, sum)    
    }
}

Ответ 9

Если вы хотите просто перебрать диапазон без использования индексов или чего-либо еще, этот пример кода работал для меня просто отлично. Никакой дополнительной декларации не требуется, нет _. Тем не менее, не проверял производительность.

for range [N]int{} {
    // Body...
}

P.S. Самый первый день в GoLang. Пожалуйста, сделайте критику, если это неправильный подход.

Ответ 10

Вы также можете проверить github.com/wushilin/stream

Это ленивый поток, похожий на концепцию java.util.stream.

// It doesn't really allocate the 10 elements.
stream1 := stream.Range(0, 10)

// Print each element.
stream1.Each(print)

// Add 3 to each element, but it is a lazy add.
// You only add when consume the stream
stream2 := stream1.Map(func(i int) int {
    return i + 3
})

// Well, this consumes the stream => return sum of stream2.
stream2.Reduce(func(i, j int) int {
    return i + j
})

// Create stream with 5 elements
stream3 := stream.Of(1, 2, 3, 4, 5)

// Create stream from array
stream4 := stream.FromArray(arrayInput)

// Filter stream3, keep only elements that is bigger than 2,
// and return the Sum, which is 12
stream3.Filter(func(i int) bool {
    return i > 2
}).Sum()

Надеюсь это поможет