Answers:
您可以并且应该只编写一个for循环。简单,明显的代码就是Go的方式。
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
这是一个程序,可以比较到目前为止建议的两种方法
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循环。
runtime.makeslice
,而另一种则不需要-我不需要基准就可以知道它会慢很多!
runtime.makeslice
如果您要求零大小分配,则“ 是”足够聪明而不分配任何内存。但是上面仍然称呼它,并且根据您的基准,在我的机器上确实需要10ns的时间。
Mark Mishyn建议使用slice,但是当可以使用通过文字创建的数组且它更短时,没有理由make
使用for
它来创建数组并在返回的slice中使用
for i := range [5]int{} {
fmt.Println(i)
}
for range [5]int{} {
5
这里是文字,不能在运行时确定。
iter是一个非常小的程序包,它仅提供了一种语法上不同的方式来迭代整数。
for i := range iter.N(4) {
fmt.Println(i)
}
Go的作者Rob Pike 批评了它:
似乎几乎每当有人想出一种方法来避免像惯用的for循环之类的事情时,因为感觉太长或太麻烦,结果几乎总是比按预期的更短的击键次数。[...]撇开了这些“改进”带来的所有疯狂开销。
iter
版本实际上使用较少的按键,因为range
并iter
会自动完成。
for
循环不是Unix的头等公民。此外,不同于for
,seq
流到标准输出一个数字序列。是否迭代它们取决于消费者。尽管for i in $(seq 1 10); do ... done
在Shell中很常见,但这只是执行for循环的一种方法seq
,尽管它是非常常见的一种方法,但它本身也只是消耗的输出的一种方法。
i in range(10)
完全像对待的方式来构建编译器(鉴于语言规范包括此用例的范围语法)i := 0; i < 10; i++
。
这是比较Go for
语句与ForClause和range
使用该iter
包的Go 语句的基准。
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
$
尽管我对缺少此语言功能感到担忧,但您可能只想使用普通for
循环即可。当您编写更多Go代码时,您可能会比想象中的还好。
我编写了这个iter程序包 -由一个简单的惯用for
循环返回,该循环返回a上的值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.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所说的泛型一样,您实际上可能不会像您想的那样错过此功能。
您也可以查看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()
希望这可以帮助
package main
import "fmt"
func main() {
nums := []int{2, 3, 4}
for _, num := range nums {
fmt.Println(num, sum)
}
}
我在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
}
注意:我写的是为了好玩!顺便说一句,有时可能会有所帮助