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?
This question is related to
go
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
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.
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
$
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.
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.
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
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()
}
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
for _, num := range nums {
fmt.Println(num, sum)
}
}
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.
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.
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)
}
Source: Stackoverflow.com