序章

コンピュータプログラミングでは、 loop は、コードの一部を繰り返し実行するためにループするコード構造であり、多くの場合、何らかの条件が満たされるまで続きます。 コンピュータープログラミングでループを使用すると、同様のタスクを自動化して複数回繰り返すことができます。 処理する必要のあるファイルのリストがある場合、または記事の行数をカウントしたい場合を想像してみてください。 これらのタイプの問題を解決するには、コードでループを使用します。

Goでは、forループは、ループカウンターまたはループ変数に基づいてコードの繰り返し実行を実装します。 whiledoなどの複数のループ構造を持つ他のプログラミング言語とは異なり、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)
}

この状態で、i5の値よりも小さい間、ループはループを継続する必要があると述べました。

最後に、postステートメントがあります。

for i := 0; i < 5; i++ {
	fmt.Println(i)
}

postステートメントでは、i++ increment 演算子を使用して、反復が発生するたびにループ変数iを1ずつインクリメントします。

このプログラムを実行すると、出力は次のようになります。

Output
0 1 2 3 4

ループは5回実行されました。 最初に、i0に設定し、i5よりも小さいかどうかを確認しました。 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(排他的)になるため、出力は次のようになります。

Output
20 21 22 23 24

postステートメントを使用して、さまざまな値でインクリメントすることもできます。 これは、他の言語のstepに似ています。

まず、正の値を持つpostステートメントを使用しましょう。

for i := 0; i < 15; i += 3 {
	fmt.Println(i)
}

この場合、forループは、0から15までの数字が出力されるように設定されていますが、3ずつ増加するため、次のように3つおきの数字のみが出力されます。

Output
0 3 6 9 12

postステートメントの引数に負の値を使用して逆方向に反復することもできますが、それに応じて最初のステートメントと条件の引数を調整する必要があります。

for i := 100; i > 0; i -= 10 {
	fmt.Println(i)
}

ここでは、i100の初期値に設定し、i < 0の条件を使用して0で停止し、postステートメントで値を次のようにデクリメントします。 -=演算子を使用して10。 ループは100で始まり、0で終わり、反復ごとに10ずつ減少します。 これが出力で発生することがわかります。

Output
100 90 80 70 60 50 40 30 20 10

for構文から最初のステートメントとpostステートメントを除外し、条件のみを使用することもできます。 これは、条件ループとして知られているものです。

i := 0
for i < 5 {
	fmt.Println(i)
	i++
}

今回は、前のコード行でforループとは別に変数iを宣言しました。 ループには、i5より小さいかどうかを確認する条件句のみがあります。 条件が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])
	}
}

これを実行すると、次の出力が得られ、スライスの各要素が出力されます。

Output
hammerhead 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)
	}
}

この場合、リスト内の各アイテムを印刷しています。 変数isharkを使用しましたが、その変数を他の有効な変数名と呼ぶこともでき、同じ出力が得られます。

Output
0 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)
	}
}
Output
src/range-error.go:8:6: i declared and not used

iforループで宣言されていますが、使用されていないため、コンパイラは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)
	}
}
Output
hammerhead 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)
	}
}
Output
S 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)
	}
}
Output
color: 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)
		}
	}
}

このプログラムを実行すると、次の出力が表示されます。

Output
1 a b c 2 a b c 3 a b c

出力は、プログラムが1を出力して外側のループの最初の反復を完了し、次にabを出力して内側のループの完了をトリガーすることを示しています。 【X196X】連続して。 内側のループが完了すると、プログラムは外側のループの先頭に戻り、2を出力してから、内側のループ全体を再度印刷します(abc)など。

ネストされた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)
		}
	}
}
Output
0 1 2 -1 -2 -3 9 8 7

ここでネストされたforループを使用すると、スライスに含まれる個々のアイテムを反復処理できます。

結論

このチュートリアルでは、forループを宣言して使用し、Goで繰り返されるタスクを解決する方法を学びました。 また、forループの3つの異なるバリエーションと、それらをいつ使用するかについても学びました。 forループとそのフローを制御する方法の詳細については、Goでループを操作するときのBreakおよびContinueステートメントの使用を参照してください。