Goでforループを構築する方法

前書き

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

Goでは、forループは、ループカウンターまたはループ変数に基づいてコードの繰り返し実行を実装します。 whiledoなどの複数のループ構造を持つ他のプログラミング言語とは異なり、Goにはforループしかありません。 これは、同じループ構造を実現するために複数の戦略を心配する必要がないため、コードをより明確で読みやすくするのに役立ちます。 開発時の読みやすさの向上と認知負荷の軽減により、他の言語よりもコードがエラーになりにくくなります。

このチュートリアルでは、Goのforループがどのように機能するかを、その使用法の3つの主要なバリエーションを含めて学習します。 まず、さまざまなタイプのforループを作成する方法を示し、次にsequential data types in Goをループする方法を示します。 最後に、ネストされたループの使用方法を説明します。

ForClauseおよび条件ループの宣言

さまざまなユースケースを説明するために、Goでforループを作成する3つの異なる方法があり、それぞれに独自の機能があります。 これらは、ConditionForClause、またはRangeClauseforループを作成するためのものです。 このセクションでは、ForClauseおよびConditionバリアントを宣言して使用する方法を説明します。

まず、ForClauseでforループを使用する方法を見てみましょう。

ForClause loopは、initial statementconditionpost statementの順で定義されます。 これらは、次の構文で配置されます。

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ずつインクリメントします。

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

Output0
1
2
3
4

ループは5回実行されました。 最初に、i0に設定し、次にi5よりも小さいかどうかを確認しました。 iの値が5未満だったため、ループが実行され、fmt.Println(i)のアクションが実行されました。 ループが終了した後、i++のpostステートメントが呼び出され、iの値が1ずつ増加しました。

[.note]#Note:プログラミングではインデックス0から開始する傾向があるため、5つの数値が出力されますが、0〜4の範囲であることに注意してください。

0から開始するか、指定した値で終了することに限定されません。 最初のステートメントに任意の値を割り当てることができ、また、ポストステートメントの任意の値で停止できます。 これにより、必要な範囲を作成してループすることができます。

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

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

Output100
90
80
70
60
50
40
30
20
10

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

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 two three four ")はいくつかのデータを含むバッファーを宣言しています。 バッファがいつ読み取りを終了するかわからないため、句のないforループを作成します。 forループ内で、line, err := buf.ReadString(' ')を使用してバッファーから行を読み取り、バッファーからの読み取り中にエラーが発生したかどうかを確認します。 あった場合は、エラーとuse the break keyword to exit the for loopに対処します。 これらのbreakポイントを使用すると、ループを停止するための条件を含める必要はありません。

このセクションでは、ForClauseループを宣言し、それを使用して既知の値の範囲を反復処理する方法を学びました。 また、特定の条件が満たされるまで条件ループを使用して繰り返す方法も学びました。 次に、RangeClauseを使用してシーケンシャルデータ型を反復処理する方法を学習します。

RangeClauseを使用したシーケンシャルデータ型のループ

Goでは、slices, arraysstringsなどのシーケンシャルまたはコレクションデータ型の要素を反復処理するために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)
    }
}

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

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

iforループで宣言されていますが、使用されていないため、コンパイラーはi declared and not usedのエラーで応答します。 これは、変数を宣言して使用しないときにGoで受け取るエラーと同じです。

このため、Goにはアンダースコア(_)であるblank identifierがあります。 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

mapを反復処理すると、rangekeyvalueの両方を返します。

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

[.note]#Note:マップが返される順序はランダムであることに注意することが重要です。 このプログラムを実行するたびに、異なる結果が得られる場合があります。

rangeforループを使用してシーケンシャルデータを反復処理する方法を学習したので、ループ内でループを使用する方法を見てみましょう。

ネストされたForループ

ループは、他のプログラミング言語と同様に、Goでネストできます。 Nestingは、ある構成が別の構成の中にある場合です。 この場合、ネストされたループは、別のループ内で発生するループです。 これらは、データセットのすべての要素でループアクションを実行する場合に役立ちます。

ネストされたループは、構造的にnested if statementsに似ています。 それらは次のように構築されます:

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を出力することによって外部ループの最初の反復を完了し、次に内部ループの完了をトリガーして、abcを連続して出力することを示しています。 。 内側のループが完了すると、プログラムは外側のループの先頭に戻り、2を出力してから、内側のループ全体(abcを再度出力します。 )s)など。

ネストされた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ループとそのフローを制御する方法の詳細については、Using Break and Continue Statements When Working with Loops in Goを参照してください。