有没有办法在Golang中迭代整个范围?

Golang的范围可以遍历地图和切片,但是我想知道是否有一种方法来迭代一系列数字,像这样

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

或者有没有一种方法来表示整数的范围像ruby一样呢?

你可以,也应该写一个for循环。 简单明显的代码就是Go的方式。

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

这是一个比较迄今为止build议的两种方法的程序

 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 

这是简单的(我已经从列表中删除了非指令)

build立

 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 , 

这里是with_iter

build立

 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解决scheme也相当昂贵。 在循环阶段,在循环中有一个额外的指令,但不是太糟糕。

我会用简单的for循环。

iter是一个非常小的包,只是提供了一个合成不同的方式来迭代整数。

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

Rob Pike(Go的一位作者) 批评了这一点 :

似乎几乎每次有人想办法避免像循环这样惯用的方式,因为它感觉太长或繁琐,结果几乎总是更多的击键,而不是更短的东西。 这就是所有这些“改善”带来的疯狂开销。

下面是一个基准,将使用iter包的For for语句与ForClause和Go range语句进行比较。

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 < bN; i++ { for j = 0; j < loops; j++ { j = j } } _ = j } func BenchmarkRangeIter(b *testing.B) { b.ReportAllocs() j := 0 for i := 0; i < bN; 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 < bN; 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 $ 

有一个丑陋的解决scheme,但它更接近于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++ {} for i:=0; i<N; i++ {} for i:=0; i<N; i++ {}循环或iter包。

虽然我担心缺乏这种语言function,但您可能只想使用正常for循环。 当你写更多的Go代码时,你可能会比你想象的更好。

我写了这个iter包 – 这是一个简单的,惯用for循环,返回一个chan int返回值的支持 – 试图改进https://github.com/bradfitz/iter中的devise,有caching和性能问题,以及一个聪明,但奇怪和不直观的实现。; 我自己的版本以同样的方式运作:

 package main import ( "fmt" "github.com/drgrib/iter" ) func main() { for i := range iter.N(10) { fmt.Println(i) } } 

然而,基准testing表明,使用频道是一个非常昂贵的select。 这三种方法的比较,可以从我的包中使用iter_test.go运行

 go test -bench=. -run=. 

量化它的performance有多糟糕

 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 

在这个过程中,这个基准testing还显示了bradfitz解决scheme与循环大小为10的内置for子句相比performance不佳。

简而言之,目前似乎还没有发现复制内置for子句的性能,同时为Python和Ruby中提供的[0,n)提供简单的语法。

这是一个耻辱,因为Go团队可能很容易向编译器添加一条简单的规则来改变一条线

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

for i := 0; i < 10; i++相同的机器码for i := 0; i < 10; i++ for i := 0; i < 10; i++ for i := 0; i < 10; i++

然而,为了公平起见,在写完自己的iter.N (但在做基准testing之前),我通过最近编写的程序回顾了所有可以使用它的地方。 其实并不多。 只有一个地方,在我的代码非重要部分,我可以通过没有更完整的,默认for条款。

所以虽然看起来这对原则上的语言来说是一个巨大的失望,但是你可能会发现,就像我一样,实际上你并不需要它。 就像罗伯·派克(Rob Pike)对generics所说的那样,你可能不会像你想像的那样错过这个function。

你也可以看看github.com/wushilin/stream

这是一个像java.util.stream的概念一样的惰性stream。

 import "github.com/wushilin/stream" stream1 := stream.Range(0, 10) // it doesn't really allocate the // 10 elements stream1.Each(print) // => print each element stream2 := stream1.Map(func(i int) int) { return i + 3 }) // Add 3 to each element, but it is a lazy add. // You only add when consume the stream stream2.Reduce(func(i, j int) int { return i + j }) // well, this consumes the stream => return sum of stream2 stream3 := stream.Of(1, 2,3,4,5) // create stream with 5 elements stream4 := stream.FromArray(arrayInput) // create stream from array stream3.Filter(func(i int) bool { return i > 2 }).Sum() => Filter stream3, keep only elements that is bigger than //2, and return the Sum, which is 12 

希望这可以帮助