コンテンツにスキップ

Go/range

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Go

Goの range キーワードは、配列、スライス、文字列、マップ、チャネルといった様々なコレクション型の要素を簡潔に反復処理するための強力な構文です。for ループと組み合わせて使用され、Goプログラミングにおいて欠かせない機能の一つと言えるでしょう。

基本構文

[編集]
for インデックス, 要素 := range コレクション {
    // 繰り返し処理
}

range 式で指定されたコレクションの要素数だけループが実行され、各イテレーションにおいて、第一の変数には要素のインデックス(またはキー)、第二の変数には要素の値が代入されます。第二の変数が不要な場合は _(ブランク識別子)で省略できます。

range が使える対象とその動作

[編集]

range は以下の型のコレクションに対して使用でき、それぞれ異なる動作をします。

  • 配列・スライス・ポインタ to 配列:
    • 第一変数: 要素のインデックス(int 型)
    • 第二変数: 要素の値
  • 文字列:
    • 第一変数: バイトインデックス(int 型)
    • 第二変数: Unicode符号点 (rune 型)
  • マップ:
    • 第一変数: キー
    • 第二変数: 値
  • チャネル:
    • 第一変数: チャネルから受信した値
    • (第二変数はありません)
  • 整数値(Go 1.22以降):
    • 第一変数: 0 から n-1 までの整数値(nrange の後の整数値)
    • (第二変数はありません)
  • イテレータ関数(Go 1.23以降):
    • 関数のシグネチャによって、一つまたは複数の値が返されます。

使用例

[編集]
配列やスライスの要素とインデックスを出力
package main

import "fmt"

func main() {
	var a = [3]string{"apple", "banana", "cherry"}
	for i, s := range a {
		fmt.Printf("インデックス: %d, 値: %s\n", i, s)
	}
	fmt.Println()

	// 文字列の各Unicode符号点とそのバイトインデックスを出力
	for i, r := range "こんにちは" {
		fmt.Printf("%d: %c\n", i, r)
	}
	fmt.Println()

	// マップのキーと値を表示(順序は保証されません)
	m := map[string]int{"apple": 100, "banana": 200, "cherry": 300}
	for key, val := range m {
		fmt.Printf("キー: %s, 値: %d\n", key, val)
	}
	fmt.Println()

	// チャネルから受信した値を順に出力
	ch := make(chan int, 2)
	go func() {
		ch <- 1
		ch <- 2
		close(ch)
	}()
	for v := range ch {
		fmt.Println("受信:", v)
	}
	fmt.Println()

	// 0から4までの整数値を順に出力(Go 1.22以降)
	for i := range 5 {
		fmt.Println("数値:", i)
	}
	fmt.Println()

	// イテレータ関数を用いたフィボナッチ数列の生成と出力(Go 1.23以降)
	fibo := func(yield func(x int) bool) {
		f0, f1 := 0, 1
		for yield(f0) {
			f0, f1 = f1, f0+f1
		}
	}

	fmt.Print("フィボナッチ数列: ")
	for x := range fibo {
		if x >= 1000 {
			break
		}
		fmt.Printf("%d ", x)
	}
	fmt.Println()
}

特記事項

[編集]
  • 変数の省略:
    • インデックスのみが必要な場合は for i := range collection { ... } のように記述します。
    • 値のみが必要な場合は for _, v := range collection { ... } のようにブランク識別子 _ を使用します。
  • range 式の評価: range の後のコレクションの式は、ループ開始前に一度だけ評価されます。ただし、変数が1つ以下で、式や長さがコンパイル時に定数の場合は、評価が省略されることがあります。
  • マップの反復順序: マップの range による反復処理では、要素の順序は保証されません。実行ごとに順序が変わる可能性があります。
  • nil 値の扱い:
    • nil スライスや nil 配列に対して range を実行すると、ループは一度も実行されません。
    • nil マップに対して range を実行すると、ループは一度も実行されません。
    • nil チャネルに対して range を実行すると、受信操作は永遠にブロックします。
  • 変数のスコープ: := を用いて for ループ内で宣言された変数は、その for ブロック内でのみ有効です。Go 1.22以降では、ループの各イテレーションで新しい変数が作成されるため、クロージャ内で変数を参照する際の注意点が変更されています。

range キーワードは、Goでコレクションデータを効率的かつ簡潔に処理するための強力なツールです。これらの特性を理解し、適切に活用することで、より可読性が高く、保守性の高いコードを書くことができるでしょう。