Go的范围可以在地图和切片上迭代,但我想知道是否有一种方法可以在数字范围上迭代,就像这样:

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

或者在Go中是否有一种方法来表示整数的范围,就像Ruby对range类所做的那样?


当前回答

Go中的惯用方法是这样写一个for循环。

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

在范围上肯定有优势,它们也被用于许多其他语言中,但Go的设计原则是,只有在收益显著超过成本(包括使语言更大的成本)时才引入抽象。理性的人不同意范围的成本和收益,但这个答案是我试图描述我认为的惯用围棋是什么。

其他回答

package main

import "fmt"

func main() {

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

问题不在于范围,而在于如何计算切片的末端。 对于固定的数字10,简单的for循环是可以的,但是对于像bfl.Size()这样的计算大小,你在每次迭代时都得到一个函数调用。大于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()
  }

以下是一个紧凑的动态版本,不依赖于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)
    }
}

通过一些调整,大小可以是uint64类型(如果需要),但这是要点。

虽然我很同情您对缺少这个语言特性的担忧,但您可能只想使用普通的for循环。当你编写更多的Go代码时,你可能会比你想象的更能接受这一点。

我编写了这个iter包——它由一个简单的、习惯的for循环支持,该循环通过chan int返回值——试图改进https://github.com/bradfitz/iter中的设计,该设计已被指出存在缓存和性能问题,以及一个聪明但奇怪且不直观的实现。我自己的版本也是这样:

package main

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

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

然而,基准测试显示,使用通道是一个非常昂贵的选择。可以从iter_test运行的3个方法的比较。放进我的包里

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

在这个过程中,这个基准测试还显示了与内置的for子句相比,对于大小为10的循环,bradfitz解决方案的性能如何不佳。

简而言之,到目前为止,似乎还没有发现一种方法可以复制内置for子句的性能,同时为[0,n)提供一种简单的语法,就像在Python和Ruby中发现的那样。

这是一个遗憾,因为Go团队可能很容易向编译器添加一个简单的规则来更改一行,如

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

与I:= 0相同的机器代码;I < 10;我+ +。

然而,公平地说,在我自己写完iter之后。N(但在对其进行基准测试之前),我回顾了最近编写的程序,以查看所有可以使用它的地方。实际上并不多。在我的代码的非重要部分中,只有一个地方不需要使用更完整的default for子句。

因此,虽然从原则上看,这对语言来说是一个巨大的失望,但您可能会发现——就像我所做的那样——实际上在实践中并不真正需要它。就像Rob Pike所说的泛型一样,您可能不会像您想象的那样想念这个特性。

我用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
}

注意:我只是为了好玩才写的!顺便说一下,有时候可能会有帮助