Golang的范围可以迭代地图和切片,但我想知道是否有办法迭代一系列数字,就像这样
for i := range [1..10] {
fmt.Println(i)
}
或者有没有办法在Go中表示整数范围,就像红宝石一样?
答案 0 :(得分:173)
你可以而且应该只写一个for循环。简单明了的代码是Go方式。
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
答案 1 :(得分:33)
这是一个比较目前建议的两种方式的程序
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 ,
这是with_iter
设置
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解决方案相当昂贵,即使它在设置阶段完全内联。在循环阶段,循环中有一条额外的指令,但它不是太糟糕。
我会使用简单的for循环。
答案 2 :(得分:20)
Mark Mishyn建议使用切片,但没有理由使用make
创建数组,并且当使用通过文字创建的数组可以使用for
返回的切片时使用它并且#39} ; s更短
for i := range [5]int{} {
fmt.Println(i)
}
答案 3 :(得分:12)
iter是一个非常小的包,只提供了一种以不同的方式迭代整数。
for i := range iter.N(4) {
fmt.Println(i)
}
Rob Pike(Go的作者)has criticized it:
似乎几乎每次有人想出办法避免 做一些类似for循环的惯用方式,因为感觉 太长或太麻烦,结果几乎总是更多的按键 而不是那个应该更短的东西。 [...]这就是把这些“改进”带来的所有疯狂开销放在一边。
答案 4 :(得分:8)
以下是使用for
包将Go range
语句与ForClause和Go 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
$
答案 5 :(得分:3)
虽然我对您缺乏此语言功能的担忧表示同意,但您可能只想使用正常的for
循环。当你写更多Go代码时,你可能会比你想象的还要好。
我写了this iter package - 由一个简单的,惯用的for
循环支持,该循环返回chan int
上的值 - 以尝试改进{{3}中的设计已被指出具有缓存和性能问题,以及一个聪明但奇怪且不直观的实现。我自己的版本运行方式相同:
package main
import (
"fmt"
"github.com/drgrib/iter"
)
func main() {
for i := range iter.N(10) {
fmt.Println(i)
}
}
然而,基准测试表明,使用频道是一种非常昂贵的选择。 3种方法的比较,可以使用
从我的包中的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
子句的情况下使用。
因此虽然看起来这对原则上的语言来说可能是一个巨大的失望,但你可能会发现 - 就像我做的那样 - 你实际上并不需要它。就像Rob Pike对于仿制药一样,你可能不会像你想象的那样错过这个功能。
答案 6 :(得分:3)
如果您只想在不使用和索引的范围内进行迭代,则此代码示例对我来说效果很好。不需要额外的声明,也不需要_
。不过,还没有检查性能。
for range [N]int{} {
// Body...
}
P.S。 GoLang的第一天。如果方法错误,请批评。
答案 7 :(得分:1)
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
for _, num := range nums {
fmt.Println(num, sum)
}
}
答案 8 :(得分:0)
您也可以退房 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()
希望这有帮助
答案 9 :(得分:0)
我已经用Golang编写了一个模仿Python的range函数的包:
包裹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
}
注:我写的很有趣!顺便说一句,有时可能会有用
答案 10 :(得分:0)
这是一个紧凑的动态版本,它不依赖于.redweek
{
color: #f00;
}
(但工作原理类似):
iter
通过一些调整,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)
}
}
的类型可能为size
(如果需要),但这是要点。
答案 11 :(得分:0)
问题不是 范围,问题是切片的 end 的计算方式。
使用固定的数字10
,简单的for
循环就可以了,但是像size
这样的计算 bfl.Size()
,每次迭代都会调用一个函数。在range
上使用简单的int32
会有所帮助,因为这样只会对bfl.Size()
进行一次评估。
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()
}