はじめに
TIG所属の大江です。Go1.24リリース連載の4本目です。
本記事では以下の内容を取り上げます。
text/template
でのrange over func、range over intのサポート
アップデート内容
Go1.22で導入されたrange over int
, Go1.23で導入されたrange over func
が共にtext/template
パッケージで使えるようになりました。
以降では、これらの機能がどのように使えるかを見ていきます。
range over int
指定された回数、同じ文字を繰り返すテンプレートを書く
Go1.23までの場合
テンプレートを使って同じ文字を10回繰り返す場合、以下のようにスライスや配列を生成してからテンプレートに渡す必要がありました。
package main
import ( "log" "os" "text/template" )
func main() { numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
tmpl := `{{range .}}Bonjour {{end}}`
t := template.Must(template.New("").Parse(tmpl)) err := t.Execute(os.Stdout, numbers) if err != nil { log.Fatal(err) } }
|
Go1.24の場合
Go1.24では、range over intにより、以下のようにスライスや配列を用意せず直接テンプレート内で繰り返し回数を指定することが出来るようになりました。
package main
import ( "log" "os" "text/template" )
func main() { tmpl := `{{range 10}}Bonjour {{end}}`
t := template.Must(template.New("").Parse(tmpl)) err := t.Execute(os.Stdout, nil) if err != nil { log.Fatal(err) } }
|
range over func
マップの中の一部を抜き出してテンプレートを使って表示する
Go1.23までの場合
以下では数字と文字の組み合わせの中から、偶数、3の倍数をそれぞれ抜き出し、テンプレートを使って表示します。
Go1.23までは以下のように、それぞれの関数の戻り値として別のマップに代入するなどしてからテンプレートに渡す必要がありました。
package main
import ( "fmt" "log" "os" "text/template" )
func main() { numbers := map[int]string{ 1: "un", 2: "deux", 3: "trois", 4: "quatre", 5: "cinq", 6: "six", 7: "sept", 8: "huit", 9: "neuf", 10: "dix", }
tmpl := `{{range $i, $c := .}}{{$i}}:{{$c}} {{end}}`
t := template.Must(template.New("").Parse(tmpl)) fmt.Println("偶数") evenNumbers := getMultiples(numbers, 2) err := t.Execute(os.Stdout, evenNumbers) if err != nil { log.Fatal(err) } fmt.Println("\n3の倍数") threeMultiples := getMultiples(numbers, 3) err = t.Execute(os.Stdout, threeMultiples) if err != nil { log.Fatal(err) } }
func getMultiples(numbers map[int]string, divisor int) map[int]string { multiples := make(map[int]string, 10) for k, v := range numbers { if k%divisor == 0 { multiples[k] = v } } return multiples }
|
Go1.24の場合
新たに導入されたrange over funcを使えば、直接テンプレート内で関数を呼び出し、戻り値を表示することができます。
なおrange over funcについては、Go1.23連載の棚井さんの記事を参考にさせていただきました。
package main
import ( "fmt" "iter" "log" "os" "text/template" )
func main() { numbers := map[int]string{ 1: "un", 2: "deux", 3: "trois", 4: "quatre", 5: "cinq", 6: "six", 7: "sept", 8: "huit", 9: "neuf", 10: "dix", }
tmpl := `{{range $i, $c := .}}{{$i}}:{{$c}} {{end}}`
t := template.Must(template.New("").Parse(tmpl)) fmt.Println("偶数") err := t.Execute(os.Stdout, getMultiples(numbers, 2)) if err != nil { log.Fatal(err) } fmt.Println("\n3の倍数") err = t.Execute(os.Stdout, getMultiples(numbers, 3)) if err != nil { log.Fatal(err) } }
func getMultiples(numbers map[int]string, divisor int) iter.Seq2[int, string] { return func(yield func(int, string) bool) { for k, v := range numbers { if k%divisor == 0 { if !yield(k, v) { return } } } } }
|
おわりに
以上、text/templateパッケージでのrange over int、range over funcについて紹介しました。近年のGoの反復処理のアップデートがさらに加速し、痒い所に手が届くようになった印象です。