有没有办法迭代Golang中的一系列整数?

时间:2014-02-22 05:39:16

标签: go

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

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

或者有没有办法在Go中表示整数范围,就像红宝石一样?

12 个答案:

答案 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()
  }
相关问题