GoでForループを構築する方法
序章
コンピュータプログラミングでは、 loop は、コードの一部を繰り返し実行するためにループするコード構造であり、多くの場合、何らかの条件が満たされるまで続きます。 コンピュータープログラミングでループを使用すると、同様のタスクを自動化して複数回繰り返すことができます。 処理する必要のあるファイルのリストがある場合、または記事の行数をカウントしたい場合を想像してみてください。 これらのタイプの問題を解決するには、コードでループを使用します。
Goでは、for
ループは、ループカウンターまたはループ変数に基づいてコードの繰り返し実行を実装します。 while
、do
などの複数のループ構造を持つ他のプログラミング言語とは異なり、Goにはfor
ループしかありません。 これは、同じループ構造を実現するために複数の戦略を心配する必要がないため、コードをより明確で読みやすくするのに役立ちます。 この読みやすさの向上と開発中の認知的負荷の軽減により、コードは他の言語よりもエラーが発生しにくくなります。
このチュートリアルでは、Goのfor
ループがどのように機能するかを、その使用法の3つの主要なバリエーションを含めて学習します。 まず、さまざまなタイプのfor
ループを作成する方法を示し、次にGoでシーケンシャルデータタイプをループする方法を示します。 最後に、ネストされたループの使用方法について説明します。
ForClauseおよびConditionループの宣言
さまざまなユースケースを説明するために、Goでfor
ループを作成する方法は3つあり、それぞれに独自の機能があります。 これらは、 Condition 、 ForClause 、またはRangeClauseを使用してfor
ループを作成するためのものです。 このセクションでは、ForClauseおよびConditionバリアントを宣言して使用する方法について説明します。
まず、ForClauseでfor
ループを使用する方法を見てみましょう。
ForClauseループは、初期ステートメント、条件、後ステートメントの順であると定義されています。 これらは次の構文で配置されます。
for [ Initial Statement ] ; [ Condition ] ; [ Post Statement ] { [Action] }
上記のコンポーネントの機能を説明するために、ForClause構文を使用して指定された範囲の値をインクリメントするfor
ループを見てみましょう。
for i := 0; i < 5; i++ { fmt.Println(i) }
このループを分解して、各部分を特定しましょう。
ループの最初の部分はi := 0
です。 これは最初のステートメントです:
for i := 0; i < 5; i++ { fmt.Println(i) }
i
という変数を宣言し、初期値を0
に設定していることを示しています。
次は条件です:
for i := 0; i < 5; i++ { fmt.Println(i) }
この状態で、i
が5
の値よりも小さい間、ループはループを継続する必要があると述べました。
最後に、postステートメントがあります。
for i := 0; i < 5; i++ { fmt.Println(i) }
postステートメントでは、i++
increment 演算子を使用して、反復が発生するたびにループ変数i
を1ずつインクリメントします。
このプログラムを実行すると、出力は次のようになります。
Output0 1 2 3 4
ループは5回実行されました。 最初に、i
を0
に設定し、i
が5
よりも小さいかどうかを確認しました。 i
の値が5
より小さいため、ループが実行され、fmt.Println(i)
のアクションが実行されました。 ループが終了した後、i++
のpostステートメントが呼び出され、i
の値が1ずつ増加しました。
注:プログラミングではインデックス0から開始する傾向があるため、5つの数値が出力されますが、0〜4の範囲であることに注意してください。
0から開始したり、指定した値で終了したりすることに限定されません。 最初のステートメントに任意の値を割り当てることができ、postステートメントの任意の値で停止することもできます。 これにより、ループスルーする任意の範囲を作成できます。
for i := 20; i < 25; i++ { fmt.Println(i) }
ここでは、反復は20(包括的)から25(排他的)になるため、出力は次のようになります。
Output20 21 22 23 24
postステートメントを使用して、さまざまな値でインクリメントすることもできます。 これは、他の言語のstep
に似ています。
まず、正の値を持つpostステートメントを使用しましょう。
for i := 0; i < 15; i += 3 { fmt.Println(i) }
この場合、for
ループは、0から15までの数字が出力されるように設定されていますが、3ずつ増加するため、次のように3つおきの数字のみが出力されます。
Output0 3 6 9 12
postステートメントの引数に負の値を使用して逆方向に反復することもできますが、それに応じて最初のステートメントと条件の引数を調整する必要があります。
for i := 100; i > 0; i -= 10 { fmt.Println(i) }
ここでは、i
を100
の初期値に設定し、i < 0
の条件を使用して0
で停止し、postステートメントで値を次のようにデクリメントします。 -=
演算子を使用して10。 ループは100
で始まり、0
で終わり、反復ごとに10ずつ減少します。 これが出力で発生することがわかります。
Output100 90 80 70 60 50 40 30 20 10
for
構文から最初のステートメントとpostステートメントを除外し、条件のみを使用することもできます。 これは、条件ループとして知られているものです。
i := 0 for i < 5 { fmt.Println(i) i++ }
今回は、前のコード行でfor
ループとは別に変数i
を宣言しました。 ループには、i
が5
より小さいかどうかを確認する条件句のみがあります。 条件がtrue
と評価される限り、ループは繰り返され続けます。
特定のタスクを完了するために必要な反復回数がわからない場合があります。 その場合、すべてのステートメントを省略し、break
キーワードを使用して実行を終了できます。
for { if someCondition { break } // do action here }
この例としては、 buffer のようなサイズが不定の構造から読み取りを行っていて、いつ読み取りが完了するかわからない場合があります。
buffer.go
package main import ( "bytes" "fmt" "io" ) func main() { buf := bytes.NewBufferString("one\ntwo\nthree\nfour\n") for { line, err := buf.ReadString('\n') if err != nil { if err == io.EOF { fmt.Print(line) break } fmt.Println(err) break } fmt.Print(line) } }
上記のコードでは、buf :=bytes.NewBufferString("one\ntwo\nthree\nfour\n")
はいくつかのデータを含むバッファーを宣言しています。 バッファがいつ読み取りを終了するかわからないため、句なしでfor
ループを作成します。 for
ループ内で、line, err := buf.ReadString('\n')
を使用してバッファーから行を読み取り、バッファーからの読み取り中にエラーが発生したかどうかを確認します。 存在する場合は、エラーに対処し、breakキーワードを使用してforループを終了します。 これらのbreak
ポイントを使用すると、ループを停止するための条件を含める必要はありません。
このセクションでは、ForClauseループを宣言し、それを使用して既知の範囲の値を反復処理する方法を学習しました。 また、Conditionループを使用して、特定の条件が満たされるまで反復する方法も学びました。 次に、RangeClauseを使用してシーケンシャルデータ型を反復処理する方法を学習します。
RangeClauseを使用したシーケンシャルデータ型のループ
Goでは、for
ループを使用して、スライス、配列、文字列などのシーケンシャルまたはコレクションデータ型の要素を反復処理するのが一般的です。 これを簡単にするために、RangeClause構文でfor
ループを使用できます。 ForClause構文を使用してシーケンシャルデータ型をループすることができますが、RangeClauseはよりクリーンで読みやすくなっています。
RangeClauseの使用を検討する前に、ForClause構文を使用してスライスを反復処理する方法を見てみましょう。
main.go
package main import "fmt" func main() { sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"} for i := 0; i < len(sharks); i++ { fmt.Println(sharks[i]) } }
これを実行すると、次の出力が得られ、スライスの各要素が出力されます。
Outputhammerhead great white dogfish frilled bullhead requiem
次に、RangeClauseを使用して、同じ一連のアクションを実行してみましょう。
main.go
package main import "fmt" func main() { sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"} for i, shark := range sharks { fmt.Println(i, shark) } }
この場合、リスト内の各アイテムを印刷しています。 変数i
とshark
を使用しましたが、その変数を他の有効な変数名と呼ぶこともでき、同じ出力が得られます。
Output0 hammerhead 1 great white 2 dogfish 3 frilled 4 bullhead 5 requiem
スライスでrange
を使用すると、常に2つの値が返されます。 最初の値はループの現在の反復が含まれるインデックスになり、2番目の値はそのインデックスの値になります。 この場合、最初の反復では、インデックスは0
であり、値はhammerhead
でした。
場合によっては、インデックスではなく、スライス要素内の値のみが必要になります。 ただし、上記のコードを変更して値のみを出力すると、コンパイル時エラーが発生します。
main.go
package main import "fmt" func main() { sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"} for i, shark := range sharks { fmt.Println(shark) } }
Outputsrc/range-error.go:8:6: i declared and not used
i
はfor
ループで宣言されていますが、使用されていないため、コンパイラはi declared and not used
のエラーで応答します。 これは、変数を宣言して使用しない場合にGoで受け取るエラーと同じです。
このため、Goにはアンダースコア(_
)である空白の識別子があります。 for
ループでは、空白の識別子を使用して、range
キーワードから返された値を無視できます。 この場合、返される最初の引数であるインデックスを無視します。
main.go
package main import "fmt" func main() { sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"} for _, shark := range sharks { fmt.Println(shark) } }
Outputhammerhead great white dogfish frilled bullhead requiem
この出力は、for
ループが文字列のスライスを繰り返し処理し、インデックスなしでスライスから各アイテムを印刷したことを示しています。
range
を使用して、リストにアイテムを追加することもできます。
main.go
package main import "fmt" func main() { sharks := []string{"hammerhead", "great white", "dogfish", "frilled", "bullhead", "requiem"} for range sharks { sharks = append(sharks, "shark") } fmt.Printf("%q\n", sharks) }
Output['hammerhead', 'great white', 'dogfish', 'frilled', 'bullhead', 'requiem', 'shark', 'shark', 'shark', 'shark', 'shark', 'shark']
ここでは、sharks
スライスの長さの各アイテムに"shark"
のプレースホルダー文字列を追加しました。
range
演算子からの戻り値を無視するために、空白の識別子_
を使用する必要がなかったことに注意してください。 Goを使用すると、どちらの戻り値も使用する必要がない場合は、range
ステートメントの宣言部分全体を省略できます。
range
演算子を使用して、スライスの値を入力することもできます。
main.go
package main import "fmt" func main() { integers := make([]int, 10) fmt.Println(integers) for i := range integers { integers[i] = i } fmt.Println(integers) }
この例では、スライスintegers
は10個の空の値で初期化されますが、for
ループは、次のようにリスト内のすべての値を設定します。
Output[0 0 0 0 0 0 0 0 0 0] [0 1 2 3 4 5 6 7 8 9]
スライスintegers
の値を初めて印刷すると、すべてゼロが表示されます。 次に、各インデックスを反復処理し、値を現在のインデックスに設定します。 次に、integers
の値をもう一度印刷すると、すべての値が0
から9
になっていることがわかります。
range
演算子を使用して、文字列内の各文字を反復処理することもできます。
main.go
package main import "fmt" func main() { sammy := "Sammy" for _, letter := range sammy { fmt.Printf("%c\n", letter) } }
OutputS a m m y
マップを反復処理すると、range
はキーと値の両方を返します。
main.go
package main import "fmt" func main() { sammyShark := map[string]string{"name": "Sammy", "animal": "shark", "color": "blue", "location": "ocean"} for key, value := range sammyShark { fmt.Println(key + ": " + value) } }
Outputcolor: blue location: ocean name: Sammy animal: shark
注:マップが返される順序はランダムであることに注意することが重要です。 このプログラムを実行するたびに、異なる結果が得られる場合があります。
range
for
ループを使用してシーケンシャルデータを反復処理する方法を学習したので、ループ内でループを使用する方法を見てみましょう。
ネストされたForループ
ループは、他のプログラミング言語と同様に、Goでネストできます。 ネストは、ある構成が別の構成の中にある場合です。 この場合、ネストされたループは、別のループ内で発生するループです。 これらは、データセットのすべての要素に対してループアクションを実行する場合に役立ちます。
ネストされたループは、ネストされたifステートメントと構造的に類似しています。 それらは次のように構成されています。
for { [Action] for { [Action] } }
プログラムは最初に外側のループに遭遇し、最初の反復を実行します。 この最初の反復により、ネストされた内部ループがトリガーされ、最後まで実行されます。 次に、プログラムは外側のループの先頭に戻り、2回目の反復を完了して、ネストされたループを再度トリガーします。 この場合も、ネストされたループは完了するまで実行され、シーケンスが完了するか、ブレークまたは他のステートメントによってプロセスが中断されるまで、プログラムは外側のループの先頭に戻ります。
ネストされたfor
ループを実装して、詳しく見てみましょう。 この例では、外側のループはnumList
と呼ばれる整数のスライスを反復処理し、内側のループはalphaList
と呼ばれる文字列のスライスを反復処理します。
main.go
package main import "fmt" func main() { numList := []int{1, 2, 3} alphaList := []string{"a", "b", "c"} for _, i := range numList { fmt.Println(i) for _, letter := range alphaList { fmt.Println(letter) } } }
このプログラムを実行すると、次の出力が表示されます。
Output1 a b c 2 a b c 3 a b c
出力は、プログラムが1
を出力して外側のループの最初の反復を完了し、次にa
、b
、を出力して内側のループの完了をトリガーすることを示しています。 【X196X】連続して。 内側のループが完了すると、プログラムは外側のループの先頭に戻り、2
を出力してから、内側のループ全体を再度印刷します(a
、b
、 c
)など。
ネストされたfor
ループは、スライスで構成されるスライス内のアイテムを反復処理するのに役立ちます。 スライスで構成されるスライスで、for
ループを1つだけ使用すると、プログラムは各内部リストをアイテムとして出力します。
main.go
package main import "fmt" func main() { ints := [][]int{ []int{0, 1, 2}, []int{-1, -2, -3}, []int{9, 8, 7}, } for _, i := range ints { fmt.Println(i) } }
Output[0 1 2] [-1 -2 -3] [9 8 7]
内部スライスの個々のアイテムにアクセスするために、ネストされたfor
ループを実装します。
main.go
package main import "fmt" func main() { ints := [][]int{ []int{0, 1, 2}, []int{-1, -2, -3}, []int{9, 8, 7}, } for _, i := range ints { for _, j := range i { fmt.Println(j) } } }
Output0 1 2 -1 -2 -3 9 8 7
ここでネストされたfor
ループを使用すると、スライスに含まれる個々のアイテムを反復処理できます。
結論
このチュートリアルでは、for
ループを宣言して使用し、Goで繰り返されるタスクを解決する方法を学びました。 また、for
ループの3つの異なるバリエーションと、それらをいつ使用するかについても学びました。 for
ループとそのフローを制御する方法の詳細については、Goでループを操作するときのBreakおよびContinueステートメントの使用を参照してください。