Slide 1

Slide 1 text

The Go gopher was designed by Renée French. The gopher stickers was made by Takuya Ueda. Licensed under the Creative Commons 3.0 Attributions license. まずはイテレータ (range over func)の 仕様を学ぼう 2024/09/24 Goのイテレータ深堀りNight https://tenn.in/iternight 1

Slide 2

Slide 2 text

UEDA Takuya / tenntenn 上田拓 也 newmo株式会社 / ソフトウェアエンジニア 2013年よりGo Conferenceの運営を行う。 
 2016年、メルカリグループに入社、Goコミュニティへの貢献や採用・社内教育などに従事。 
 2021年、一般社団法人Gophers Japan設立、代表理事に就任。Google Developers Expert (GDE)に選出。 
 2022年、株式会社ナレッジワーク入社。ソフトウェアエンジニアおよびGoエンジニアのイネーブルメントに従事。 
 2024年、newmo株式会社入社。ソフトウェアエンジニアとしてプロダクト開発に従事。 
 略歴

Slide 3

Slide 3 text

newmoにnew社しました! https://note.com/newmohq/n/nd6401c84b9f5

Slide 4

Slide 4 text

イテレータの導入経緯 Go1.18 Go1.21 Go1.23 ジェネリクス (型パラメータ) の導入 slices, maps パッケージの導入 イテレータ (range over func) の導入 stdは変更最小限 maps.Keysは 導入されず stdは変更最小限 ※ std: 標準ライブラリ ジェネリクスの導入の議論の中でイテレータという概念が生じ、別の議論へと発展した

Slide 5

Slide 5 text

型パラメータのおさらい パラメータ 関数パラメータ 型パラメータ 外から変更できるもの func F(n int) { fmt.Println(n) } F(10) func F() { fmt.Println(10) } func G[T any](v T) { fmt.Println(v) } G[int](10) // G(10) func G(v int) { fmt.Println(v) } 仮引数 実引数 な し あ り 指 定 型パラメータ 型引数

Slide 6

Slide 6 text

イテレータ( range over func) 1 2 3 seq0 := func(yield func() bool) {} for range seq0 {} seq1 := func(yield func(X) bool) {} for x := range seq1 {} seq2 := func(yield func(X, Y) bool) {} for x, y := range seq2 {} for range文に関数が指定できるようになった

Slide 7

Slide 7 text

range over funcと他のrange over 対象 こ れ ま で 関 数 振る舞い [N]E, *[N]E, []E, map[K]V, string, chan E, <-chan E, N func(func() bool) func(func(X) bool) func(func(X, Y) bool) 型ごとに決められている 例:先頭から順に 関数によって実装する 例:後方から、深さ優先探索 イテレーションの仕方が自由になった

Slide 8

Slide 8 text

重要な3つの仕様 1 for range文に指定した関数が 1度だけ実行される 2 yield関数を呼ぶと for文のボディに処理が移る 3 for range文が途中で終了すると yield関数がfalseを返す for range func call for range func yield for range func false break return

Slide 9

Slide 9 text

例:アルファベット列の生成 func Alphabet(yield func(rune) bool) { for c := 'A'; c <= 'Z'; c++ { // 'A', 'B', 'C', … if !yield(c) { return } } } func usage() { // ABC for c := range Alphabet { fmt.Printf("%c", c) if c == 'C' { break } } }

Slide 10

Slide 10 text

標準ライブラリの変更点 1 2 type Seq[V any] func(yield func(V) bool) type Seq2[K, V any] func(yield func(K, V) bool) iterパッケージと iter.Seq型とiter.Seq2型が導入された ※ iter.Pull関数とiter.Pull2関数についてはeihighさんにお任せします!

Slide 11

Slide 11 text

例:マップ関数 func Map[X, Y any](seq iter.Seq[X], f func(X) Y) iter.Seq[Y] { return func(yield func(Y) bool) { for x := range seq { if !yield(f(x)) { break } } } } func usage() { seq := Map(slices.Values([]int{10, 20}), func(x int) string { return fmt.Sprintf("0x%x", x) }) // 0xa // 0x14 for v := range seq { fmt.Println(v) } }

Slide 12

Slide 12 text

注意点 1 yieldは宣言済み識別子(組み込み関数)でもなく、 予約語(キーワード)でもない 2 yield関数がfalseを返したのに引き続き yield関数を呼ぶと パニックが発生する 3 for range文にnilなイテレータを指定すると パニックが発生する

Slide 13

Slide 13 text

newmo 技術ブログ @newmotech