Open6

Go 1.23 で新しく入った range-over-func, iter packageについて理解する

masafumi330masafumi330

たとえば、Iterator を回して valueを取得して、もしその value の値が 1だったら、for loopを抜けるなど。

masafumi330masafumi330

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型の関数を返す関数である」と言える。

masafumi330masafumi330

上の続き

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に責務を押し付けることができるようになった。

masafumi330masafumi330

例えば、整数のスライスを引数に受け取って、偶数のみをフィルタリングしてイテレータとして値を返してく関数を書いてみるとか。

masafumi330masafumi330

yield とは?

type CallbackFunc func(yield func(int, string) bool)

構文を解説
何のことはない。「名前付き返り値( Named return values )」だった。
https://go-tour-jp.appspot.com/basics/7

上の例は、 「 func(int, string) bool 型の関数型を返す。ただしその名前は yield である 」 としているだけだ。

イメージ図