🗺️

Goでmap関数を実装してみる

2024/09/07に公開

TL;DR

package main

import (
	"fmt"
	"iter"
	"slices"
)

func Map[S, T any, C func(S) T](c C, src iter.Seq[S]) iter.Seq[T] {
	return func(yield func(T) bool) {
		for s := range src {
			if !yield(c(s)) {
				break
			}
		}
	}
}

func twice(n int) int {
	return 2 * n
}

func main() {
	numbers := []int{1, 2, 3, 4, 5}
	result := slices.Collect(Map(twice, slices.Values(numbers)))

	fmt.Printf("%+v\n", result)
}

https://go.dev/play/p/5WbDK5Z-Q74

Goでもmap()を使いたい

PythonとかRustとかTypeScriptで使えるmapが便利なのでGoでも使いたくなります。
Go 1.23.0でイテレータを扱えるようになり、Pythonのmap関数と同等の機能が実現できるようになったのでやってみます。もう何番煎じかわかりませんが...

第一引数に変換関数を、第二引数にイテレータを受け取って、各要素を変換したイテレータを返します。シグネチャはこんな感じです。
(SとTのインターフェース制約はもうちょっと考えた方がいいかも?)

func Map[S, T any, C func(S) T](c C, src iter.Seq[S]) iter.Seq[T] {
    ...
}

中身はこんな感じです。srcをイテレートして、変換関数Cを適用してyieldするだけです。

func Map[S, T any, C func(S) T](c C, src iter.Seq[S]) iter.Seq[T] {
	return func(yield func(T) bool) {
		for s := range src {
			if !yield(c(s)) {
				break
			}
		}
	}
}

スライスの各要素を変換して新たなスライスを作りたいというとき、今までは変換関数とスライスを受け取ってそれを適用する関数をそれぞれ用意しなければなりませんでしたが、イテレータが使えるようになったことで変換関数を1つ用意すればジェネリックに使えるようになりました。(ジェネリクス使えば変換関数は共通化できましたが)
複数の変換関数をチェインすることもできるので便利になったなあと思います。

GitHubで編集を提案

Discussion