Open6
Go 1.23 で新しく入った range-over-func, iter packageについて理解する
なぜIter が有用なのか?
- 「構造を順番に回すこと」と「それぞれの振る舞い」を分離できる
- 途中でStopできる
たとえば、Iterator を回して valueを取得して、もしその value の値が 1だったら、for loopを抜けるなど。
range-over-func
- for-rangeループのrange句は、次の型のイテレータ関数を受け取れるようになった。
func(func() bool)
func(func(K) bool)
func(func(K, V) bool)
つまり、
func Backward(s []string) func(yield func(int, string) bool) {
return func(yield func(int, string) bool) {
for i := len(s) - 1; i >= 0; i-- {
if !yield(i, s[i]) {
return
}
}
}
}
func main() {
s := []string{"hello", "world"}
for i, x := range Backward(s) {
fmt.Println(i, x)
}
}
のように、ユーザー定義の独自のコールバック関数を持つイテレータをrange句に指定できるようになった。
もうちょっと分かりやすくしてみると、
type CallbackFunc func(yield func(int, string) bool)
func Backward(s []string) CallbackFunc {
return func(yield func(int, string) bool) {
for i := len(s) - 1; i >= 0; i-- {
if !yield(i, s[i]) {
return
}
}
}
}
「関数 Backward は、string型のスライスを引数にとり、 CallbackFunc型の関数を返す関数である」と言える。
上の続き
func main() {
s := []string{"hello", "world"}
for i, x := range Backward(s) {
fmt.Println(i, x)
}
}
for ... range で繰り返されるループ処理は、関数 Backwardから与えられる値を iterate over (反復処理する)
この時、ループの中では通常のセマンティクス
- break
- return
- continue
- その他の制御フロー (if文とか?)
を使えるのが結構でかいんじゃないかと思う。
「ただ繰り返す」っていう処理と、「終わる」「もしこうだったら続ける」というロジックを分離できた。
=> この例で言うと、 繰り返し方 は、関数Backwardに責務を押し付けることができるようになった。
例えば、整数のスライスを引数に受け取って、偶数のみをフィルタリングしてイテレータとして値を返してく関数を書いてみるとか。
yield とは?
type CallbackFunc func(yield func(int, string) bool)
構文を解説
何のことはない。「名前付き返り値( Named return values )」だった。
上の例は、 「 func(int, string) bool 型の関数型を返す。ただしその名前は yield である 」 としているだけだ。
イメージ図
