sort.Interface
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 74/107。
sort パッケージは、インターフェースベースの設計のもう一つの優れた例を提供します。カスタムコレクションをソートするには、独自の型が sort.Interface を実装する必要があります:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}これら3つのメソッドは、ソートアルゴリズムに必要なすべての情報(コレクションの長さ、要素を比較する方法、および要素を入れ替える方法)を提供します。カスタム構造体のスライスをソート可能にする方法は次のとおりです。
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() で動作するようになります。
チャレンジ
簡単Goのsort.Interfaceの強力さを示す、ソート可能な商品在庫システムを構築しましょう!Product型を作成し、複数のソート戦略を実装することで、同じコレクションを異なる方法でソートできるようにします。
コードは2つのファイルに分けて構成します:
product.go: 商品型とソートの実装を定義します。3つのフィールドを持つ
Product構造体を作成します:Name(string)、Price(float64)、Quantity(int)。[]Productに基づいた2つの名前付き型を作成します:ByPrice- 商品を価格の昇順でソートするためByQuantity- 商品を在庫数の降順(在庫数が多い順)でソートするため
各型は、
sort.Interfaceで要求される3つのメソッドを実装する必要があります:Len()、Less(i, j int)、およびSwap(i, j int)。Lessメソッドが各型のソート順序を決定します。main.go: 商品在庫を構築し、ソートします。ソートモード(
priceまたはquantity)を読み取り、次に商品数、続いて商品の詳細を読み取ります。各商品は、名前、価格、在庫数の3行で提供されます。商品のスライスを作成し、モードに基づいて適切なソート型を使用してソートし、各商品を次の形式で出力します:
[Name]: $[Price] (x[Quantity])価格は小数点以下2桁まで表示してください。
以下の入力が提供されます:
- 1行目:ソートモード(
priceまたはquantity) - 2行目:商品の数
- 続く行:商品の詳細(名前、価格、在庫数 - 1商品につき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)異なる名前付き型を使用するだけで、同じ商品データを異なる方法でソートできることに注目してください。型がsort.Interfaceを満たせば、標準ライブラリのsort.Sort()とシームレスに連携します。
チートシート
sort パッケージでは、型をソート可能にするために sort.Interface を実装する必要があります。
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}カスタムスライスをソート可能にするには、名前付き型を作成し、必要な3つのメソッドを実装します。
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] }
// Sort the collection
sort.Sort(ByAge(people))同じスライスに対して複数の名前付き型を作成し、異なるソート動作を実装することができます。各型は、ソート順を決定するために独自の Less メソッドを定義します。
型が sort.Interface を満たすと、sort.Sort()、sort.Reverse()、および sort.IsSorted() で使用できるようになります。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
)
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 = name[:len(name)-1] // 改行を削除
priceStr, _ := reader.ReadString('\n')
priceStr = priceStr[:len(priceStr)-1]
price, _ := strconv.ParseFloat(priceStr, 64)
qtyStr, _ := reader.ReadString('\n')
qtyStr = qtyStr[:len(qtyStr)-1]
quantity, _ := strconv.Atoi(qtyStr)
products[i] = Product{Name: name, Price: price, Quantity: quantity}
}
// TODO: モードに基づいて商品をソートする
// モードが "price" の場合は ByPrice 型を使用する
// モードが "quantity" の場合は ByQuantity 型を使用する
// 適切な型で sort.Sort() を使用する
// TODO: 各商品を以下の形式で出力する:
// [Name]: $[Price] (x[Quantity])
// 価格のフォーマットには fmt.Printf と %.2f を使用する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー11標準ライブラリとOOP
io.Reader と io.Writersort.Interfacefmt.Stringer インターフェースencoding/json と構造体http.Handler インターフェースまとめ:REST API モデル