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

紹介

コンピュータープログラミングにおいて、ループは、コードを繰り返して実行する構文を持つ構造であり、一般的に、いくつかの条件が満たされるまで続く。コンピュータープログラミングでのループの使用により、同じような作業を自動化し、何度も繰り返すことができます。たとえば、処理する必要があるファイルのリストがあったり、記事の行数を数えたいときには、そのような問題を解決するためにコード内のループを使用します。

Go言語では、forループを使って、 loop counterやloop variableに基づいてコードを繰り返して実行することができます。whiledoなどの他のプログラミング言語のように複数のループ構文を持つものではなく、Goにはforループしか存在しません。これにより、あなたのコードがより明確で可読性が高くなり、同じループ構造を実現するための複数の戦略を心配する必要がなくなります。開発中の知的負荷を軽減し、他の言語よりもエラーに対する抵抗力が高まることから、読む人にとってもより良い結果をもたらします。

このチュートリアルで、Goのforループの仕組みを学ぶことができます。それには、その使用の3つの主要な変形が含まれます。まず、異なる種類のforループを作成する方法を見ていき、その後、Goでの連続的なデータ型をループしていく方法を学びます。最後に、嵌套されたループの使用方法を説明します。

ForClauseと条件ループの宣言

多様な使用例を考慮するために、Goでforループを作成するための3つの異なる方法があり、それぞれ独自の機能を持っています。これらはforループを条件として作成する、ForClauseを使用する、またはRangeClauseを使用することです。この節で、ForClauseと条件変数の宣言と使用法を説明します。

まず、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以下であれば、ループを続けることを指定しています。

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

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

postステートメントでは、iという루프変数をi++を使用して每次の迭代時に1つ上にincrementします。

このプログラムを実行すると、以下のような出力が表示されます:

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

私たちは、後置声明の引数にネガティブの値を使用して、後退的に繰り返すこともできますが、初期ステートメントと条件引数を適切に調整する必要があります。

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

ここで、初期値を100に設定し、条件をi < 0に設定して0まで行くようにし、後置ステートメントで10つの値を減らす-=运算子を使用します。ループは100から始まり、0まで行き、各イテレーションで10つの値を減少させます。出力にこれが反映されることがわかります。

Output
100 90 80 70 60 50 40 30 20 10

また、初期ステートメントと後置ステートメントをforスyntaxから除外し、条件だけを使用することもできます。これは条件ループと呼ばれます。

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

この回、変数iforループの前の行の中で別々に宣言しました。ループには条件の節だけがあり、i5以下かどうかを確認します。条件がtrueと評価される間、ループはイテレーションを続けます。

特定の任务を完了するために必要な繰り返し回数を知らない場合もあります。その場合、すべてのステートメントを省略し、breakキーワードを使用して実行を終了することができます。

for {
	if someCondition {
break
}
	// ここに动作を行います
}

この例では、不确定な大きさの構造(例えばバッファ)から読み取る際に、完了するまでを知らないような場合に使われるかもしれません。

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ポイントで、for文を停止する条件を含む必要はありません。

この節で、ForClauseのループを宣言し、既知の範囲の値を迭代する方法を学びました。また、特定の条件を満たすまでに迭代するConditionループの使用方法も学びました。次に、RangeClauseが順序的なデータ型を迭代するための使用方法を学びます。

RangeClauseを使用して順序的なデータ型をループする

Go言語では、for ループを使用して、slice、配列、stringなどの連続的なまたは集合的なデータ型の要素をイテレートすることが一般的です。これをより簡単にするために、for ループをRangeClause 構文とともに使用することができます。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)
	}
}

この場合、リストの各アイテムを印刷しています。変数iおよびsharkを使用したとしても、他の有効な変数名を変数に割り当てることができ、同じ出力が得られます:

Output
0 hammerhead 1 great white 2 dogfish 3 frilled 4 bullhead 5 requiem

スライス上でrangeを使用すると、常に2つの値を返します。最初の値は、現在のループのイテレーションにおけるインデックスであり、2番目の値はそのインデックスにある値です。この場合、最初のイテレーションでは、インデックスは0であり、そのインデックスの値はhammerheadでした。

sometimes、sliceの要素値を取り出したいときは、索引を使用しないでください。前のコードを変更して、値だけをprint outするようにしたら、compile timeエラーが発生します:

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

becauseiis declared in theforloop, but never used, the compiler will respond with the error ofi declared and not used. This is the same error that you will receive in Go anytime you declare a variable and don’t use it.

Because of this, Go has theblank identifier(underscore)(_). In aforloop, you can use the blank identifier to ignore any value returned from therange keyword. In this case, we want to ignore the index, which is the first argument returned.

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

This output shows that theforloop iterated through the slice of strings, and printed each item from the slice without the index.

You can also userangeto add items to a list:

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']

Here, we have added a placeholder string of"shark"for each item of the length of thesharksslice.

Notice that we didn’t have to use the blank identifier_to ignore any of the return values from therangeoperator. Go allows us to leave out the entire declaration portion of therangestatement if we don’t need to use either of the return values.

We can also use therangeoperator to fill in values of a slice:

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

この例では、slice `integers` は10個の空値を初期化しますが、for ループは繰り返しているindexに対応する値を全て設定します:

Output
[0 0 0 0 0 0 0 0 0 0] [0 1 2 3 4 5 6 7 8 9]

初回で slice `integers` の値をprintした時、全ての値は0です。次に、loopで各indexをiterateして、それぞれに現在のindex値をsetします。次回 print 時、`integers` の値は全て0から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

注意:重要的是要注意,map返回的顺序是随机的。每次运行这个程序,你可能会得到不同的结果。

现在我们已经学会了如何使用 rangefor 循环来迭代序列数据,让我们来看一下如何使用嵌套循环。

嵌套 for 循环

Go言語でも他のプログラミング言語と同様にネストされたループを使用できます。ネストとは、一つの構造の中に別の構造を入れ替えることを指します。この場合、ネストされたループは、他のループの中で発生するループです。これは、データセットのすべての要素に対してループされる动作を実行したい場合に有用です。

ネストされたループは、ネストされたifと構造的に似ています。それは以下のように構築されます。

for {
    [Action]
    for {
        [Action]  
    }
}

プログラムはまず外側のループに遭遇し、最初のイテレーションを実行します。この最初のイテレーションは、内部的なネストされたループをトリガーし、完了まで実行します。次に、プログラムは外側のループの頂点に戻り、2番目のイテレーションを完了し、再び内部的なネストされたループをトリガーします。再び、内部的なネストされたループは完了し、プログラムは外側のループの頂点に戻ります。これが完了するまでまたはbreak句などのステートメントがプロセスを破壊するまでに繰り返されます。

次に、より詳細にネストされた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回目のiterationで1を出力し、それが内部ループを触发する。内部ループは次々にabcを连续に出力します。外部ループの顶端に戻って来た後、2を出力し、再度内部ループを全て出力します(abc) など。

内蔵されたfor loopは、内蔵されたsliceをiterateすることができます。単一のfor loopを使用していた場合、外部listは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]

要は、内部slicesの個別itemを取り出すには、nestedfor loopを使う必要があります:

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

このように、nestedfor loopを使用することで、外部slices内の各itemをiterateできます。

結論

本教程ではGoで繰り返し処理を行う方法を見ていました。私たちは3種類のfor loopの变体を見ていましたから、それらを使う時の違いも知っておくと良いです。繰り返し処理を制御するには、breakcontinueステンションを使うこともできます。詳細はGoで繰り返し処理を行う時に break 和 continue ステンションを使うを読みましょう。

Source:
https://www.digitalocean.com/community/tutorials/how-to-construct-for-loops-in-go