Is there a way to iterate over a range of integers?

LoopsGoIntegerRange

Loops Problem Overview


Go's range can iterate over maps and slices, but I was wondering if there is a way to iterate over a range of numbers, something like this:

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

Or is there a way to represent range of integers in Go like how Ruby does with the class Range?

Loops Solutions


Solution 1 - Loops

The idiomatic approach in Go is to write a for loop like this.

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

There's definitely advantages in ranges and they're used in many other languages, but a Go design principle is to only introduce an abstraction if the benefits significantly outweigh the costs (including the cost of making the language larger). Reasonable people disagree about the costs and benefits of ranges, but this answer is my attempt to describe what I think idiomatic Go is.

Solution 2 - Loops

Here is a program to compare the two ways suggested so far

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

Compile like this to generate disassembly

go build -gcflags -S iter.go

Here is plain (I've removed the non instructions from the listing)

setup

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

loop

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     ,

And here is with_iter

setup

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

loop

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     ,

So you can see that the iter solution is considerably more expensive even though it is fully inlined in the setup phase. In the loop phase there is an extra instruction in the loop, but it isn't too bad.

I'd use the simple for loop.

Solution 3 - Loops

It was suggested by Mark Mishyn to use slice but there is no reason to create array with make and use in for returned slice of it when array created via literal can be used and it's shorter

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

Solution 4 - Loops

iter is a very small package that just provides a syntantically different way to iterate over integers.

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

Rob Pike (an author of Go) has criticized it:

> It seems that almost every time someone comes up with a way to avoid doing something like a for loop the idiomatic way, because it feels too long or cumbersome, the result is almost always more keystrokes than the thing that is supposedly shorter. [...] That's leaving aside all the crazy overhead these "improvements" bring.

Solution 5 - Loops

Here's a benchmark to compare a Go for statement with a ForClause and a Go range statement using the iter package.

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
}

Output:

$ 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
$

Solution 6 - Loops

If you want to just iterate over a range w/o using and indices or anything else, this code sample worked just fine for me. No extra declaration needed, no _. Haven't checked the performance, though.

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

P.S. The very first day in GoLang. Please, do critique if it's a wrong approach.

Solution 7 - Loops

While I commiserate with your concern about lacking this language feature, you're probably just going to want to use a normal for loop. And you'll probably be more okay with that than you think as you write more Go code.

I wrote this iter package — which is backed by a simple, idiomatic for loop that returns values over a chan int — in an attempt to improve on the design found in https://github.com/bradfitz/iter, which has been pointed out to have caching and performance issues, as well as a clever, but strange and unintuitive implementation. My own version operates the same way:

package main

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

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

However, benchmarking revealed that the use of a channel was a very expensive option. The comparison of the 3 methods, which can be run from iter_test.go in my package using

go test -bench=. -run=.

quantifies just how poor its performance is

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

In the process, this benchmark also shows how the bradfitz solution underperforms in comparison to the built-in for clause for a loop size of 10.

In short, there appears to be no way discovered so far to duplicate the performance of the built-in for clause while providing a simple syntax for [0,n) like the one found in Python and Ruby.

Which is a shame because it would probably be easy for the Go team to add a simple rule to the compiler to change a line like

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

to the same machine code as for i := 0; i < 10; i++.

However, to be fair, after writing my own iter.N (but before benchmarking it), I went back through a recently written program to see all the places I could use it. There actually weren't many. There was only one spot, in a non-vital section of my code, where I could get by without the more complete, default for clause.

So while it may look like this is a huge disappointment for the language in principle, you may find — like I did — that you actually don't really need it in practice. Like Rob Pike is known to say for generics, you might not actually miss this feature as much as you think you will.

Solution 8 - Loops

You can also check out github.com/wushilin/stream

It is a lazy stream like concept of 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()

Hope this helps

Solution 9 - Loops

I have written a package in Golang which mimic the Python's range function:

Package https://github.com/thedevsaddam/iter

package main

import (
	"fmt"

	"github.com/thedevsaddam/iter"
)

func main() {
	// sequence: 0-9
	for v := range iter.N(10) {
		fmt.Printf("%d ", v)
	}
	fmt.Println()
	// output: 0 1 2 3 4 5 6 7 8 9

	// sequence: 5-9
	for v := range iter.N(5, 10) {
		fmt.Printf("%d ", v)
	}
	fmt.Println()
	// output: 5 6 7 8 9

	// sequence: 1-9, increment by 2
	for v := range iter.N(5, 10, 2) {
		fmt.Printf("%d ", v)
	}
	fmt.Println()
	// output: 5 7 9

	// sequence: a-e
	for v := range iter.L('a', 'e') {
		fmt.Printf("%s ", string(v))
	}
	fmt.Println()
	// output: a b c d e
}

> Note: I have written for fun! Btw, sometimes it may be helpful

Solution 10 - Loops

Here is a compact, dynamic version that doesn't depend on iter (but works similarly):

package main

import (
	"fmt"
)

// N is an alias for an unallocated struct
func N(size int) []struct{} {
	return make([]struct{}, size)
}

func main() {
	size := 1000
	for i := range N(size) {
		fmt.Println(i)
	}
}

With some tweaks size could be of type uint64 (if needed) but that's the gist.

Solution 11 - Loops

The problem is not the range, the problem is how the end of slice is calculated. with a fixed number 10 the simple for loop is ok but with a calculated size like bfl.Size() you get a function-call on every iteration. A simple range over int32 would help because this evaluate the bfl.Size() only once.

type BFLT PerfServer   
  func (this *BFLT) Call() {
    bfl := MqBufferLCreateTLS(0)                                                                                   
    for this.ReadItemExists() {                                                                                    
      bfl.AppendU(this.ReadU())                                                                                    
    }
    this.SendSTART()
    // size := bfl.Size() 
    for i := int32(0); i < bfl.Size() /* size */; i++ {                                                                             
      this.SendU(bfl.IndexGet(i))                                                                                  
    }
    this.SendRETURN()
  }

Solution 12 - Loops

package main

import "fmt"

func main() {

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVishnuView Question on Stackoverflow
Solution 1 - LoopsPaul HankinView Answer on Stackoverflow
Solution 2 - LoopsNick Craig-WoodView Answer on Stackoverflow
Solution 3 - LoopsDaniil GrankinView Answer on Stackoverflow
Solution 4 - LoopselithrarView Answer on Stackoverflow
Solution 5 - LoopspeterSOView Answer on Stackoverflow
Solution 6 - LoopsWHSView Answer on Stackoverflow
Solution 7 - LoopsChris RedfordView Answer on Stackoverflow
Solution 8 - LoopsWu ShilinView Answer on Stackoverflow
Solution 9 - LoopsSaddam HossainView Answer on Stackoverflow
Solution 10 - LoopsMarco ChiappettaView Answer on Stackoverflow
Solution 11 - LoopsAndreas OttoView Answer on Stackoverflow
Solution 12 - LoopsDvv AvinashView Answer on Stackoverflow