回答:
forループを作成できます。シンプルでわかりやすいコードがGoの方法です。
for i := 1; i <= 10; i++ {
fmt.Println(i)
}
これは、これまでに提案された2つの方法を比較するプログラムです
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
、もう1つは呼び出しません-それがずっと遅くなることを知るのにベンチマークは必要ありません!
runtime.makeslice
あなたはゼロサイズの割り当てを要求した場合に任意のメモリを割り当てるためではない賢い十分です。しかし、上記はまだそれを呼び出しており、あなたのベンチマークによれば、私のマシンでは10nS長くかかります。
Mark Mishynがスライスを使用することを提案しましたが、リテラルを介して作成された配列を使用でき、それが短い場合はmake
、for
返されたスライスで配列を作成して使用する理由はありません。
for i := range [5]int{} {
fmt.Println(i)
}
for range [5]int{} {
5
ここがリテラルであり、実行時に決定できないことです。
iterは、整数を反復処理するための構文的に異なる方法を提供する非常に小さなパッケージです。
for i := range iter.N(4) {
fmt.Println(i)
}
Rob Pike(Goの作者)はそれを批判しました:
誰かがforループのようなことをするのを避ける方法を思いつくたびに、それはあまりにも長く感じたり面倒だと感じたりするので、ほとんど常に、おそらく短いものよりもキーストロークが多くなります。[...]それは、これらの「改善」がもたらすクレイジーなオーバーヘッドをすべて残しています。
iter
バージョンがオートコンプリートするためrange
、実際には使用するキーストロークが少なくなりiter
ます。
for
ループは、Unixの第一級市民ではありません。さらに、とは異なりfor
、seq
標準へのストリームは一連の数値を出力します。それらを反復するかどうかは、消費者次第です。for i in $(seq 1 10); do ... done
シェルでは一般的ですが、それはforループを実行する1つの方法にすぎません。これはseq
、非常に一般的な方法ですが、それ自体がの出力を消費する1つの方法にすぎません。
i in range(10)
まったく同じように構築される可能性があるという事実を単に考慮していませんi := 0; i < 10; i++
。
これは、パッケージを使用して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
$
私はこの言語機能の欠如についての懸念を表明しますが、おそらく通常の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.go
を使用してパッケージで実行できる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
このプロセスでは、このベンチマークは、ループサイズがのbradfitz
組み込みfor
句と比較して、ソリューションのパフォーマンスがどのように低いかを示しています10
。
要するに、PythonやRubyに見られるようfor
な単純な構文を提供しながら、組み込みの句のパフォーマンスを複製する方法は今のところ発見されていないよう[0,n)
です。
Goチームがコンパイラに簡単なルールを追加して次のような行を変更するのはおそらく簡単なので、これは残念です。
for i := range 10 {
fmt.Println(i)
}
と同じマシンコードにfor i := 0; i < 10; i++
。
しかし、公平を期すために、自分で作成した後iter.N
(ただしベンチマークの前)、最近作成したプログラムに戻って、使用できるすべての場所を確認しました。実際には多くありませんでした。私のコードの重要ではないセクションには、より完全なデフォルトのfor
句なしで済む場所が1つしかありませんでした。
だから、これは原則として言語にとって大きな失望のように見えるかもしれませんが、私がしたように、実際には実際にはそれを必要としないかもしれません。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)
}
}
Pythonのrange関数を模倣するパッケージをGolangで作成しました。
パッケージ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
}
注:私は楽しみのために書きました!ところで、時にはそれが役立つかもしれません