[インデックス 11954] ファイルの概要
このコミットは、Go言語の標準ライブラリである sort パッケージに、sort.Interface の使用例を追加するものです。具体的には、カスタム型をソートする方法と、既存の sort.Interface を利用して逆順ソートを実現する方法を示す新しいテストファイルが追加されています。これにより、sort パッケージの利用者が、より簡単に独自のデータ構造をソートできるよう、具体的なコード例が提供されます。
コミット
- コミットハッシュ:
8bb7f7791b20d6d1b287e728b76ef95a8dd6af7c - Author: Andrew Gerrand adg@golang.org
- Date: Thu Feb 16 13:16:07 2012 +1100
- 変更ファイル:
src/pkg/sort/example_interface_test.go(新規追加)src/pkg/sort/example_reverse_test.go(新規追加)
- 変更行数: 2ファイルで合計107行の追加
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8bb7f7791b20d6d1b287e728b76ef95a8dd6af7c
元コミット内容
sort: add interface examples
R=golang-dev, bradfitz, r
CC=golang-dev
https://golang.org/cl/5677060
変更の背景
Go言語の sort パッケージは、スライスやカスタムコレクションをソートするための強力な機能を提供します。しかし、その中心となる sort.Interface の概念は、Go言語に慣れていない開発者にとっては直感的ではない場合があります。特に、Len(), Less(i, j int), Swap(i, j int) の3つのメソッドを実装する必要があるため、具体的な使用例が不足していると、どのようにカスタム型をソートすれば良いか迷うことがあります。
このコミットは、このような背景から、sort.Interface の具体的な実装例と、それを利用したソートのデモンストレーションを提供することを目的としています。これにより、開発者が sort パッケージをより効果的に活用し、カスタムデータ構造のソートを容易に行えるようになることが期待されます。また、Reverse のような汎用的な逆順ソートの例も提供することで、より高度なソート要件にも対応しやすくなります。
前提知識の解説
Go言語の sort パッケージ
Go言語の sort パッケージは、組み込み型(int, float64, string)のスライスをソートするための関数(sort.Ints, sort.Float64s, sort.Strings)を提供しますが、最も重要なのはカスタム型をソートするための sort.Interface インターフェースです。
sort.Interface インターフェース
sort.Interface は以下の3つのメソッドを定義しています。
Len() int: ソート対象の要素数を返します。Less(i, j int) bool: インデックスiの要素がインデックスjの要素よりも「小さい」(ソート順で前に来る)場合にtrueを返します。このメソッドがソートの基準を決定します。Swap(i, j int): インデックスiとインデックスjの要素を入れ替えます。
カスタム型をソートするには、そのカスタム型(またはそのカスタム型をラップする型)が sort.Interface を実装する必要があります。
Go言語の fmt パッケージ
fmt パッケージは、フォーマットされたI/O(入力/出力)を実装するためのパッケージです。fmt.Println や fmt.Printf などを使用して、標準出力に文字列や変数の値を出力するために広く使われます。
fmt.Sprintf(format string, a ...interface{}) string: フォーマット文字列と引数に基づいて文字列を生成し、その文字列を返します。fmt.Printf(format string, a ...interface{}) (n int, err error): フォーマット文字列と引数に基づいて文字列を生成し、標準出力に出力します。fmt.Println(a ...interface{}) (n int, err error): 引数をスペースで区切り、改行を追加して標準出力に出力します。
また、カスタム型が String() string メソッドを実装している場合、fmt パッケージのフォーマット動詞(例: %v)はそのメソッドを呼び出して値を文字列として表現します。これは、デバッグ出力やユーザーフレンドリーな表示に非常に便利です。
Go言語の埋め込み(Embedded Structs)
Go言語では、構造体の中に別の構造体をフィールドとして宣言することで、その埋め込まれた構造体のメソッドを外側の構造体が「継承」したかのように振る舞わせることができます。これは、コードの再利用性を高めるための強力なメカニズムです。
例えば、type ByName struct { Organs } のように Organs を埋め込むと、ByName 型のインスタンスは Organs 型が持つ Len() や Swap() メソッドを直接呼び出すことができます。これにより、ByName は Less() メソッドだけを独自に実装すれば sort.Interface を満たすことができます。
技術的詳細
このコミットでは、sort パッケージの利用方法を具体的に示すために、2つの新しいテストファイルが追加されています。これらのファイルは、Goのテストフレームワークの Example 関数として機能し、ドキュメントの一部として自動的に実行され、出力が検証されます。
src/pkg/sort/example_interface_test.go
このファイルは、カスタムデータ構造をソートする方法を詳細に示しています。
Grams型:intを基底とするカスタム型で、String()メソッドを実装しています。これにより、fmt.PrintfなどでGrams型の値を人間が読みやすい形式で出力できるようになります(例: "1340g")。Organ構造体:Name(string) とWeight(Grams) を持つ構造体で、ソート対象の個々の要素を表します。Organs型:[]*Organのスライス型で、sort.InterfaceのLen()とSwap()メソッドを実装しています。これにより、Organs型はソート可能なコレクションとしての基本的な振る舞いを持ちます。ByName構造体:Organsを埋め込み、Less()メソッドを実装しています。このLess()メソッドはOrganのNameフィールドに基づいて比較を行います。これにより、Organsのスライスを名前順にソートできます。ByWeight構造体:Organsを埋め込み、Less()メソッドを実装しています。このLess()メソッドはOrganのWeightフィールドに基づいて比較を行います。これにより、Organsのスライスを重さ順にソートできます。ExampleInterface()関数: 実際のソート処理と出力の例を示しています。Organのスライスを初期化します。sort.Sort(ByWeight{s})を使用して重さ順にソートし、結果を出力します。sort.Sort(ByName{s})を使用して名前順にソートし、結果を出力します。Output:コメントブロックにより、期待される出力が明示されています。これはGoのテストフレームワークが自動的に検証します。
src/pkg/sort/example_reverse_test.go
このファイルは、既存の sort.Interface の実装を逆順にソートする方法を示しています。
Reverse構造体:sort.Interfaceを埋め込んでいます。Less(i, j int) boolメソッド: 埋め込まれたsort.InterfaceのLessメソッドを呼び出し、その結果を反転させます (r.Interface.Less(j, i))。これにより、元のソート順とは逆の順序でソートが行われます。ExampleInterface_reverse()関数: 実際の逆順ソートの例を示しています。intのスライスを初期化します。sort.Sort(Reverse{sort.IntSlice(s)})を使用して逆順にソートします。sort.IntSliceは[]intをsort.Interfaceに適合させるためのアダプターです。- 結果を出力し、期待される出力 (
[6 5 4 3 2 1]) がOutput:コメントブロックで示されています。
これらの例は、sort.Interface の柔軟性と、Goのインターフェースと埋め込みの強力な組み合わせを示しています。
コアとなるコードの変更箇所
src/pkg/sort/example_interface_test.go
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort_test
import (
"fmt"
"sort"
)
type Grams int
func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) }
type Organ struct {
Name string
Weight Grams
}
type Organs []*Organ
func (s Organs) Len() int { return len(s) }
func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// ByName implements sort.Interface by providing Less and using the Len and
// Swap methods of the embedded Organs value.
type ByName struct{ Organs }
func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name }
// ByWeight implements sort.Interface by providing Less and using the Len and
// Swap methods of the embedded Organs value.
type ByWeight struct{ Organs }
func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight }
func ExampleInterface() {
s := []*Organ{
{"brain", 1340},
{"heart", 290},
{"liver", 1494},
{"pancreas", 131},
{"prostate", 62},
{"spleen", 162},
}
sort.Sort(ByWeight{s})
fmt.Println("Organs by weight:")
printOrgans(s)
sort.Sort(ByName{s})
fmt.Println("Organs by name:")
printOrgans(s)
// Output:
// Organs by weight:
// prostate (62g)
// pancreas (131g)
// spleen (162g)
// heart (290g)
// brain (1340g)
// liver (1494g)
// Organs by name:
// brain (1340g)
// heart (290g)
// liver (1494g)
// pancreas (131g)
// prostate (62g)
// spleen (162g)
}
func printOrgans(s []*Organ) {
for _, o := range s {
fmt.Printf("%-8s (%v)\n", o.Name, o.Weight)
}
}
src/pkg/sort/example_reverse_test.go
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sort_test
import (
"fmt"
"sort"
)
// Reverse embeds a sort.Interface value and implements a reverse sort over
// that value.
type Reverse struct {
// This embedded Interface permits Reverse to use the methods of
// another Interface implementation.
sort.Interface
}
// Less returns the opposite of the embedded implementation's Less method.
func (r Reverse) Less(i, j int) bool {
return r.Interface.Less(j, i)
}
func ExampleInterface_reverse() {
s := []int{5, 2, 6, 3, 1, 4} // unsorted
sort.Sort(Reverse{sort.IntSlice(s)})
fmt.Println(s)
// Output: [6 5 4 3 2 1]
}
コアとなるコードの解説
example_interface_test.go の解説
このファイルは、Goの sort.Interface をカスタム型に適用する典型的なパターンを示しています。
-
Grams型とString()メソッド:type Grams intは、int型を基底とする新しい型Gramsを定義しています。func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) }は、Grams型がfmt.Stringerインターフェース(String() stringメソッドを持つインターフェース)を満たすようにします。これにより、fmt.Printfやfmt.PrintlnでGrams型の値を表示する際に、自動的にこのString()メソッドが呼び出され、例えば1340が1340gと表示されるようになります。これは、出力の可読性を高めるためのGoの慣用的なパターンです。 -
Organ構造体:type Organ struct { Name string; Weight Grams }は、ソート対象となる個々の「臓器」を表す構造体です。Nameは文字列、Weightは先ほど定義したGrams型です。 -
Organs型とLen(),Swap()メソッド:type Organs []*Organは、Organ構造体へのポインタのスライスをOrgansという新しい型として定義しています。func (s Organs) Len() int { return len(s) }は、スライスの長さを返します。func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }は、スライス内の2つの要素を交換します。 これらのメソッドの実装により、Organs型はsort.InterfaceのLen()とSwap()の要件を満たします。 -
ByName構造体とLess()メソッド:type ByName struct{ Organs }は、Organs型を埋め込んだ新しい構造体ByNameを定義しています。Organsを埋め込むことで、ByNameはOrgansが持つLen()とSwap()メソッドを自動的に「継承」します。func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name }は、sort.InterfaceのLess()メソッドを実装しています。このメソッドは、Organsスライス内のi番目の要素のNameがj番目の要素のNameよりも辞書順で小さい場合にtrueを返します。これにより、ByName型はsort.Interfaceの全ての要件を満たし、名前順でのソートが可能になります。 -
ByWeight構造体とLess()メソッド:type ByWeight struct{ Organs }も同様にOrgansを埋め込んでいます。func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight }は、Weightフィールドに基づいて比較を行います。これにより、ByWeight型は重さ順でのソートを可能にします。 -
ExampleInterface()関数: この関数は、上記の型定義とメソッド実装を使って実際にソートを行う例です。sort.Sort(ByWeight{s})は、OrgansスライスsをByWeight型にラップしてsort.Sort関数に渡すことで、重さ順にソートを実行します。sort.Sort(ByName{s})も同様に、名前順にソートを実行します。printOrgansヘルパー関数は、ソートされた結果を整形して出力するために使用されます。// Output:コメントブロックは、Goのテストシステムがこの例を実行した際に期待する出力を定義しており、実際の出力と一致するかどうかを検証します。
example_reverse_test.go の解説
このファイルは、既存の sort.Interface の実装を逆順にソートするための汎用的なアダプターを示しています。
-
Reverse構造体:type Reverse struct { sort.Interface }は、sort.Interfaceを埋め込んだ構造体です。これにより、Reverse型は、埋め込まれたsort.Interfaceが持つLen()とSwap()メソッドを自動的に利用できます。 -
Less(i, j int) boolメソッド:func (r Reverse) Less(i, j int) bool { return r.Interface.Less(j, i) }がこのアダプターの核心です。通常のLess(i, j)はiがjより小さい場合にtrueを返しますが、この実装ではr.Interface.Less(j, i)を呼び出しています。これは、元のLessメソッドの引数を逆にして呼び出すことで、比較結果を反転させ、結果として逆順ソートを実現します。 -
ExampleInterface_reverse()関数: この関数は、Reverseアダプターの使用例です。s := []int{5, 2, 6, 3, 1, 4}は、ソートされていない整数のスライスです。sort.Sort(Reverse{sort.IntSlice(s)})は、sort.IntSlice(s)を使用して[]intをsort.Interfaceに適合させ、それをさらにReverse型でラップしてsort.Sort関数に渡しています。これにより、整数のスライスが降順にソートされます。// Output:コメントブロックは、期待される出力[6 5 4 3 2 1]を示しています。
これらの例は、Goのインターフェースと埋め込みの強力な組み合わせが、柔軟で再利用可能なソートロジックをどのように構築できるかを示しています。
関連リンク
- Go CL 5677060: https://golang.org/cl/5677060
参考にした情報源リンク
- Go言語
sortパッケージ公式ドキュメント: https://pkg.go.dev/sort - Go言語
fmtパッケージ公式ドキュメント: https://pkg.go.dev/fmt - A Tour of Go - Interfaces: https://go.dev/tour/methods/9
- Effective Go - Embedding: https://go.dev/doc/effective_go#embedding