4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Goのジェネリクス機能を試してみた

Last updated at Posted at 2023-09-02

こんにちは!
Qiita初投稿です。

久しぶりにGoを触る機会あったのですが、Go 1.18からジェネリクス機能が追加されたということで、ジェネリクス機能を使って遊んでみました。

基本的な使い方

func display[T any](value T) {
	fmt.Println(value)
}

[T any]の部分が、任意の型(any)のT、を意味しています。
多くの言語では<>を使って書きますが、Goは[]なんですね。
anyはinterface{}の型エイリアスで、Go 1.18から追加されたものです。
受け取る型を制限したい場合は、下記のように書けます。

func sum[T int | float32 | float64](array []T) T {
	var result T
	for _, v := range array {
		result += v
	}
	return result
}

intあるいはfloatの配列を受け取って、合計値を返却する関数です。
int、float以外の配列を渡すと、型チェックでエラーになるので、安心ですね。

Listを作ってみる

Goを使う時のつらみに、配列やスライスに少し癖があって、扱いに慣れが必要なところがありました。
そこで、ジェネリクスを使って、スライスをラップした便利Listを作ってみたいと思います。

type IList[T comparable] interface {
	Add(value T)
	Remove(value T)
	Get(idx int) T
	GetIndex(value T) int
	Replace(idx int, value T)
	Count() int
	Contains(value T) bool
	Mapper(f func(T) T)
	Filter(f func(T) bool)
	Clear()
	Display()
}

まずはinterfaceを定義します。
[T comparable]のcomparableですが、比較可能な型という意味で、==等で比較が使える型に制限しています。
要素の削除などを行う際に、==での値の比較が必要になったので制限をつけました。

Add...値の追加
Remove...値の削除
Get...値の取得
GetIndex...インデックス取得
Replace...値の置き換え
Count...要素数を取得
Contains...値の存在確認
Mapper...全要素に関数を適用
Filter...条件を満たす要素をフィルタリング
Clear...要素の全削除
Display...printlnで配列を出力

それではこのインターフェイスを元に実装していきます。

type List[T comparable] struct {
	list []T
}

// 要素の追加
func (l *List[T]) Add(value T) {
	l.list = append(l.list, value)
}

// 要素の削除
func (l *List[T]) Remove(value T) {
	for i, v := range l.list {
		if v == value {
			l.list = l.list[:i+copy(l.list[i:], l.list[i+1:])]
		}
	}
}

// 要素の取得
func (l *List[T]) Get(idx int) T {
	return l.list[idx]
}

// インデックスの取得
func (l *List[T]) GetIndex(value T) int {
	for i, v := range l.list {
		if v == value {
			return i
		}
	}
	return -1
}

// 要素の置き換え
func (l *List[T]) Replace(idx int, value T) {
	l.list[idx] = value
}

// 要素数の取得
func (l *List[T]) Count() int {
	return len(l.list)
}

// 要素の存在確認
func (l *List[T]) Contains(value T) bool {
	for _, val := range l.list {
		if val == value {
			return true
		}
	}
	return false
}

// 全要素に関数適用
func (l *List[T]) Mapper(f func(T) T) {
	for i, v := range l.list {
		l.list[i] = f(v)
	}
}

// 条件を満たす要素のみフィルタリング
func (l *List[T]) Filter(f func(T) bool) {
	for i, v := range l.list {
		if !f(v) {
			l.list = l.list[:i+copy(l.list[i:], l.list[i+1:])]
		}
	}
}

// 要素の全削除
func (l *List[T]) Clear() {
	l.list = []T{}
}

// Printlnで表示
func (l *List[T]) Display() {
	fmt.Println(l.list)
}

このListを取得するための関数を用意します。

func CreateList[T comparable]() IList[T] {
	return &List[T]{}
}

使い方はこんな感じです

func main() {
    // float64でリストを作成
	list := CreateList[float64]()

    // 値を追加
	list.Add(1.0)
	list.Add(3.14)

	fmt.Println(list.Count())
	fmt.Println(list.Get(0))

    // 全要素を3倍してみる
	list.Mapper(func(f float64) float64 {
		return f * 3
	})

    list.Display()

    // 5.0より小さい数は削除する
	list.Filter(func(f float64) bool {
		return f > 5.0
	})

	list.Display()

    // リストのクリア
	list.Clear()
}

楽しく実装できました!
ジェネリクスを使って、汎用的なライブラリを作ってみたいですね。

気になるところとしては、ジェネリクスを多用するとコンパイルや実行が遅くならないかというところです。
これについては、またいつか調査してみたいと思います。

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?