A “for” statement with a “range” clause iterates through all entries of an array, slice, string or map, values received on a channel, or integer values from zero to an upper limit [Go 1.22].
Range expression に渡す値のデータ型により、ループ変数にどのような値が渡されるのかが整理されています。例えば、Range に渡したデータ型が配列やスライスであれば、1st value と 2nd value に渡される値は「int型のインデックス、インデックスに対応する値」であり、マップ型であれば「キー、キーに対応する値」という言語仕様が説明されています。
Range expression 1st value 2nd value
array or slice a [n]E, *[n]E, or []E index i int a[i] E string s string type index i int see below rune map m map[K]V key k K m[k] V channel c chan E, <-chan E element e E integer n integer type value i see below
Range の説明に see below と記載された項目のうち、integer については以下の説明が Go 1.22 から追記されています。
For an integer value n, the iteration values 0 through n-1 are produced in increasing order. If n <= 0, the loop does not run any iterations.
Range expression に自然数を渡した場合は、0からその数分だけ+1インクリメントしながらループが繰り返されていること、また、0以下の整数を渡した場合にはループ自体がスキップされていることが分かります。
Go 1.21 以前に「指定の回数だけループを繰り返したい」場合には以下のような「C言語から続く伝統的なループ処理」で実装する必要がありました。
package main
import ( "fmt" )
funcmain() { // C言語からの伝統のループ for i := 0; i < 5; i++ { fmt.Println(i) } }
「伝統的なループ処理」のコードレビューでは「あー、for で回数指定のループね。ここの処理は配列やマップに依存していないから、拡張for文は使えないのか。ループの開始値は x で、終了条件は x < y だから、ループ回数は z になる。よし、テストコード側も確認しよう」のように「境界条件を脳内でイメージした上、ループ回数を演算して、妥当性を確認する」必要がありました。今後は「伝統的なループの記述が不要」になりましたので、Go 1.22 からは少しだけレビューが楽になりました。予めループの回数が決まっている処理は range over integer で書き換えられると思いますので、発見次第リファクタリングの Pull Request を作成してみてはいかがでしょうか。
There is too much clutter here — too much code, too many concepts, too many constructs — for what the program does. … Educators often offer the admonition, “don’t worry about that, you’ll understand it later.” This is unsatisfying to them and their students alike, and leaves students with the enduring impression that Java is complicated.
と言われているのは、「それはそう」と久しぶりに仕様書で笑いました。
ここまで読んでいただけた方であれば、本記事冒頭の「Go 1.22 で追加された range over integer の仕様紹介」はメイントピックではなく、「色々なプログラミング言語を書いてみること」が執筆モチベーションになっていることをご理解いただけると思います。Go 1.22 のマイナーアップデートという「きっかけ」を利用して、多種多様なプログラミング言語を眺めてみました。