制御構造

Go言語のループ処理について解説

Go言語のループに関して、基本環境が整った方にも扱いやすいシンプルな繰り返し処理の実装方法を紹介します。

この記事では、特にコード中で使用するfor文を例に、容易に実践できるループ処理の書き方や工夫を説明します。

基本的なfor文の書き方

for文の基本構成

Go言語では、for文が唯一のループ制御文です。

基本構成は以下のようになっており、初期化、条件式、後置処理を1行にまとめて記述できます。

例えば、以下のサンプルコードは数値を順番に出力するシンプルな例です。

package main
import "fmt"
func main() {
	// インデックスの初期化、条件式、後置処理が1行に記述されています
	for i := 0; i < 5; i++ {
		fmt.Println("数値:", i) // 数値を出力する
	}
}
数値: 0
数値: 1
数値: 2
数値: 3
数値: 4

初期化、条件式、後置処理の役割

for文の3つの主要な部分は、それぞれ以下の役割を持っています。

  • 初期化: ループ開始前に1回だけ実行される処理です。変数の宣言や初期値の設定を行います。

例: i := 0

  • 条件式: 毎回ループの先頭で評価される条件です。条件がtrueの場合はループを継続し、falseになるとループ終了となります。

例: i < 5

  • 後置処理: 各ループ終了後に実行される処理です。変数の更新などを記述します。

例: i++

これらにより、シンプルなループ構造を直感的に記述することができます。

レンジを使ったループ処理

スライス・配列の反復処理

rangeキーワードを利用すると、スライスや配列の要素を簡単に順番に取得することができます。

下記のサンプルは、スライス内の各要素とインデックスを出力する例です。

package main
import "fmt"
func main() {
	// スライスの定義
	numbers := []int{10, 20, 30, 40, 50}
	// rangeを使ってインデックスと要素を取得
	for index, value := range numbers {
		fmt.Printf("インデックス:%d, 値:%d\n", index, value)
	}
}
インデックス:0, 値:10
インデックス:1, 値:20
インデックス:2, 値:30
インデックス:3, 値:40
インデックス:4, 値:50

マップのループ操作

rangeキーワードはマップのループ処理にも利用できます。

キーと値を同時に取得するため、マップの各要素に対して簡単に処理を実行できます。

package main
import "fmt"
func main() {
	// マップの定義
	cityPopulation := map[string]int{
		"Tokyo":    14000000,
		"Osaka":    8900000,
		"Nagoya":   2300000,
	}
	// rangeを使ってキーと値を取得
	for city, population := range cityPopulation {
		fmt.Printf("都市:%s, 人口:%d\n", city, population)
	}
}
都市:Tokyo, 人口:14000000
都市:Osaka, 人口:8900000
都市:Nagoya, 人口:2300000

文字列のループ処理

文字列に対してrangeを使用すると、Unicodeコードポイント(ルーン単位)で反復処理が行われます。

各文字のインデックスと値を取得できるので、日本語文字列や特殊文字を扱う場合にも便利です。

package main
import "fmt"
func main() {
	// 文字列の定義
	message := "Hello, 世界"
	// rangeを用いて文字列内の各ルーンを取得
	for index, char := range message {
		fmt.Printf("インデックス:%d, 文字:%c\n", index, char)
	}
}
インデックス:0, 文字:H
インデックス:1, 文字:e
インデックス:2, 文字:l
インデックス:3, 文字:l
インデックス:4, 文字:o
インデックス:5, 文字:,
インデックス:7, 文字:世
インデックス:10, 文字:界

制御文を利用したループ操作

break文の使用例

break文は、特定の条件でループを途中終了するために使用します。

下記のサンプルでは、ある条件に達した時点でbreak文によりループが終了される例を示します。

package main
import "fmt"
func main() {
	// 0から9までの数値を出力し、5が出たらループを終了
	for i := 0; i < 10; i++ {
		if i == 5 {
			// 5に達したのでbreakでループ終了
			break
		}
		fmt.Println("数値:", i)
	}
}
数値: 0
数値: 1
数値: 2
数値: 3
数値: 4

continue文の使用例

continue文は、特定の条件下で今のループの残りの処理をスキップし、次のループに移るときに使用します。

次のサンプルでは、偶数をスキップして奇数のみ出力します。

package main
import "fmt"
func main() {
	// 0から9までの数値のうち、偶数はスキップして奇数のみ出力
	for i := 0; i < 10; i++ {
		if i%2 == 0 {
			// 偶数の場合はこのループの後続処理をスキップ
			continue
		}
		fmt.Println("奇数:", i)
	}
}
奇数: 1
奇数: 3
奇数: 5
奇数: 7
奇数: 9

ラベル付きループの利用法

複数のループが入れ子になっている場合、ラベルを使用して外側のループを明示的に制御することができます。

以下のサンプルは、内側のループで特定の条件になった場合に、外側のループも同時に終了する例です。

package main
import "fmt"
func main() {
	// ラベルouterを定義し、2重ループを実装
outer:
	for i := 0; i < 3; i++ {
		for j := 0; j < 3; j++ {
			// 特定の条件に達したら、外側のループもbreakする
			if i == 1 && j == 1 {
				break outer
			}
			fmt.Printf("i=%d, j=%d\n", i, j)
		}
	}
}
i=0, j=0
i=0, j=1
i=0, j=2
i=1, j=0

ループの応用例と注意事項

ネストされたループの実装

入れ子ループにおける可読性向上の工夫

入れ子になったループでは、変数名やインデント、コメントを工夫することで、コードの可読性を高めることができます。

以下のサンプルは、2次元のスライスを処理する例です。

各ループの役割が明確になるように変数名やコメントを記述しています。

package main
import "fmt"
func main() {
	// 2次元スライスの定義
	matrix := [][]int{
		{1, 2, 3},
		{4, 5, 6},
		{7, 8, 9},
	}
	// 外側のループ:各行を処理
	for rowIndex, row := range matrix {
		// 内側のループ:各行内の各要素を処理
		for colIndex, element := range row {
			fmt.Printf("行:%d, 列:%d, 値:%d\n", rowIndex, colIndex, element)
		}
	}
}
行:0, 列:0, 値:1
行:0, 列:1, 値:2
行:0, 列:2, 値:3
行:1, 列:0, 値:4
行:1, 列:1, 値:5
行:1, 列:2, 値:6
行:2, 列:0, 値:7
行:2, 列:1, 値:8
行:2, 列:2, 値:9

無限ループの作成と安全な抜け方

無限ループは、条件が常にtrueとなるfor {}構文で作成できます。

安全にループを抜ける場合は、途中でbreak文などを用いてループ終了の条件を作成します。

以下のサンプルは、ユーザの入力や特定条件でループを終了するシンプルな例です。

package main
import (
	"fmt"
)
func main() {
	counter := 0
	// 無限ループを開始
	for {
		// カウンタが3に達したらループを終了
		if counter == 3 {
			break
		}
		fmt.Println("カウンタ:", counter)
		counter++
	}
}
カウンタ: 0
カウンタ: 1
カウンタ: 2

パフォーマンス考慮点

ループ内処理の最適化ポイント

ループ内で実行する処理はできるだけ効率的に記述することが大切です。

頻繁に使われる計算や関数呼び出しは、ループの外で前計算するなどの工夫が役立ちます。

ここでは具体的な数値例として、ある計算結果をループ外で定義し、不要な計算を省略する方法を示します。

package main
import "fmt"
func main() {
	// ループ外で計算結果を定義し、ループ内で再利用する例
	const factor = 2
	for i := 1; i <= 5; i++ {
		// iに対してfactorを乗算する
		result := i * factor
		fmt.Printf("入力:%d, 結果:%d\n", i, result)
	}
}
入力:1, 結果:2
入力:2, 結果:4
入力:3, 結果:6
入力:4, 結果:8
入力:5, 結果:10

リソース管理への影響と注意点

大量のループを実行する際、特にメモリやCPUリソースへの影響に注意する必要があります。

例えば、ループ内で大きなオブジェクトを生成し続けるとガベージコレクションの負荷が増加するため、必要な場合はオブジェクトの再利用やメモリの解放を意識することが重要です。

以下は、ループ内で不要な変数再生成を避ける一例です。

package main
import "fmt"
func main() {
	// 事前に必要な変数を定義してループ内で再生成しないようにする
	var baseString = "値:"
	numbers := []int{1, 2, 3, 4, 5}
	for _, num := range numbers {
		// すでに定義されているbaseStringを利用
		fmt.Println(baseString, num)
	}
}
値: 1
値: 2
値: 3
値: 4
値: 5

まとめ

この記事では、Go言語の基本的なfor文の書き方からレンジを使った各種ループ処理、制御文によるループ操作、入れ子ループや無限ループの実装、そしてパフォーマンスを考慮した最適化方法まで学びました。

全体を通して、シンプルかつ効率的なループ処理の記述方法について理解を深める内容となっています。

ぜひ、実際の開発に応用して、新たな実装方法を試してみてください。

関連記事

Back to top button
目次へ