Menu
Coddy logo textTech

ジェネリクス入門

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 67/107。

Go 1.18 より前は、異なる型で再利用可能なコードを書くには、関数を複製するか、空のインターフェース any と型アサーションを使う必要がありました。ジェネリクスは、完全な型安全性を維持しながら、複数の型で動作する関数や型を書けるようにすることで、この問題を解決します。

slice の最小値を求めることを考えてみましょう。generic がなければ、型ごとに個別の関数が必要になります。

func MinInt(values []int) int {
    min := values[0]
    for _, v := range values {
        if v < min {
            min = v
        }
    }
    return min
}

func MinFloat64(values []float64) float64 {
    min := values[0]
    for _, v := range values {
        if v < min {
            min = v
        }
    }
    return min
}

ジェネリックを使うと、角括弧内の型 parameterを使用して、ロジックを一度だけ記述できます。

func Min[T int | float64](values []T) T {
    min := values[0]
    for _, v := range values {
        if v < min {
            min = v
        }
    }
    return min
}

[T int | float64] は、int または float64 のいずれかにできる型パラメーター T を宣言します。これで、同じ関数を使って Min([]int{3, 1, 2}) または Min([]float64{3.5, 1.2}) を呼び出せます。コンパイラーはコンパイル時に型をチェックするため、実行時のオーバーヘッドなしで安全性を得られます。

これからのレッスンでは、型パラメーター、制約、ジェネリック構造体について詳しく学びます。

challenge icon

チャレンジ

簡単

genericを使って、整数と浮動小数点数のスコアの両方に対応するスコア analyzerを作りましょう!slice内の最大値をfindする単一のgeneric functionを作成し、異なる数値typeごとに重複したコードを書く必要をなくします。

コードを2つのファイルに整理します。

  • analyzer.go:generic analysis functionを定義します。

    slice内の最大値をfindしてreturnsするgeneric function Max[T int | float64](values []T) Tを作成します。この functionは、type parameter Tを通じてintfloat64の両方のtypeで動作します。

    また、slice内のall valuesのsumをcalculatesしてreturnsするgeneric function Sum[T int | float64](values []T) Tも作成します。

  • main.go:inputをReadし、異なるtypeでgeneric functionsを実演します。

    type indicator(intまたはfloat)をReadし、その後にcountと、count個のvaluesをReadします。typeに応じてvaluesをParseし、適切なtypeでMaxSumの両方をCallして、resultsをPrintします。

    integer inputの場合、次のようにPrintします。

    Max (int): [value]
    Sum (int): [value]

    float inputの場合、小数点以下1桁でPrintします。

    Max (float): [value]
    Sum (float): [value]

次のinputが提供されます。

  • Line 1:Type indicator(intまたはfloat
  • Line 2:valuesのcount(integer)
  • Following lines:1行につき1つのvalue

たとえば、次のinputが与えられた場合:

int
4
15
8
23
11

outputは次のようになります。

Max (int): 23
Sum (int): 57

また、次のinputが与えられた場合:

float
3
4.5
9.2
6.8

outputは次のようになります。

Max (float): 9.2
Sum (float): 20.5

同じgeneric functionsがinteger dataとfloating-point dataの両方をどのように処理しているかに注目してください。type parameter [T int | float64]によって、compilerは各usageに対してtype-safeなcodeを生成できます。その一方で、あなたが書くlogicは1回だけです。

自分で試してみよう

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 型インジケーターを読み取る
	typeStr, _ := reader.ReadString('\n')
	typeStr = strings.TrimSpace(typeStr)

	// 個数を読み取る
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))

	if typeStr == "int" {
		// 整数値を読み取る
		values := make([]int, count)
		for i := 0; i < count; i++ {
			line, _ := reader.ReadString('\n')
			values[i], _ = strconv.Atoi(strings.TrimSpace(line))
		}

		// TODO: 整数スライスで Max と Sum を呼び出す
		// TODO: "Max (int): [value]" および "Sum (int): [value]" の形式で結果を出力する

	} else if typeStr == "float" {
		// 浮動小数点値を読み取る
		values := make([]float64, count)
		for i := 0; i < count; i++ {
			line, _ := reader.ReadString('\n')
			values[i], _ = strconv.ParseFloat(strings.TrimSpace(line), 64)
		}

		// TODO: float64 スライスで Max と Sum を呼び出す
		// TODO: 小数点以下1桁で "Max (float): [value]" および "Sum (float): [value]" の形式で結果を出力する
	}
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Goオンラインコンパイラ