Menu
Coddy logo textTech

sort.Interface

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

sort パッケージは、インターフェースベースの設計のもう一つの優れた例を提供します。カスタムコレクションを並べ替えるには、型が sort.Interface を実装する必要があります。

type Interface interface {
    Len() int
    Less(i, j int) bool
    Swap(i, j int)
}

これら3つのメソッドによって、ソートアルゴリズムに必要なものがすべて提供されます。それは、collection の length、elements を比較する方法、そしてそれらを入れ替える方法です。カスタム構造体のスライスをソート可能にする方法は次のとおりです。

type Person struct {
    Name string
    Age  int
}

type ByAge []Person

func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }

func main() {
    people := []Person{
        {"Alice", 30},
        {"Bob", 25},
        {"Carol", 35},
    }
    
    sort.Sort(ByAge(people))
    fmt.Println(people)
    // [{Bob 25} {Alice 30} {Carol 35}]
}

重要なポイントは、スライスに基づく名前付き型(ByAge)を作成することです。これにより、同じデータに対して異なる並べ替え動作を定義できます。代わりに、別のLess実装を持つByNameを作成して、アルファベット順に並べ替えることもできます。

型が sort.Interface を満たすと、sort.Sort()sort.Reverse()sort.IsSorted() と自動的に連携して動作します。

challenge icon

チャレンジ

簡単

Go の sort.Interfaceの力を示す、ソート可能な product inventory system を構築しましょう!Product型を作成し、複数のソート戦略を実装することで、同じ collection を異なる方法でソートできるようにします。

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

  • product.go:product type とソートの実装を定義します。

    3つのフィールドを持つProduct struct を作成します。Name(string)、Price(float64)、Quantity(int)です。

    []Product based の2つの named types を作成します。

    • ByPrice - price の ascending order で products をソートするためのもの
    • ByQuantity - quantity の descending order(quantity が多いものから)で products をソートするためのもの

    各 type は、sort.Interfaceに必要な3つの methods、Len()Less(i, j int)Swap(i, j int)を実装する必要があります。Less method が各 type の sort order を決定します。

  • main.go:product inventory を構築してソートします。

    sort mode(priceまたはquantity)を読み取り、続けて count と product details を読み取ります。各 product は、name、price、quantity の3行で指定されます。

    products の slice を作成し、mode に基づいて適切な sorting type を使ってソートした後、各 product を次の format で表示します。

    [Name]: $[Price] (x[Quantity])

    prices は小数点以下2桁で表示します。

次の inputs が提供されます。

  • 1行目:Sort mode(priceまたはquantity
  • 2行目:products の number
  • 続く行:Product details(name、price、quantity - 1 product あたり3行)

たとえば、次のように指定した場合:

price
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25

出力は次のようになります。

Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)

また、次のように指定した場合:

quantity
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25

出力は次のようになります。

Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)

同じ product data でも、異なる named type を使うだけで異なる方法でソートできることに注目してください。types がsort.Interfaceを満たせば、standard library のsort.Sort()とシームレスに連携できます。

自分で試してみよう

package main

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

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

	// ソートモードを読み取る
	var mode string
	fmt.Fscanln(reader, &mode)

	// 製品の数を読み取る
	var count int
	fmt.Fscanln(reader, &count)

	// 製品を読み取る
	products := make([]Product, count)
	for i := 0; i < count; i++ {
		name, _ := reader.ReadString('\n')
		name = strings.TrimSpace(name)

		priceStr, _ := reader.ReadString('\n')
		priceStr = strings.TrimSpace(priceStr)
		price, _ := strconv.ParseFloat(priceStr, 64)

		qtyStr, _ := reader.ReadString('\n')
		qtyStr = strings.TrimSpace(qtyStr)
		quantity, _ := strconv.Atoi(qtyStr)

		products[i] = Product{Name: name, Price: price, Quantity: quantity}
	}

	// TODO: モードに基づいて製品をソートする
	// mode が "price" の場合、ByPrice 型を使用する
	// mode が "quantity" の場合、ByQuantity 型を使用する
	// 適切な型で sort.Sort() を使用する

	// TODO: 各製品を次の形式で出力する:
	// [Name]: $[Price] (x[Quantity])
	// 価格のフォーマットに %.2f を使って fmt.Printf を使用する
}
quiz icon腕試し

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

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

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