[インデックス 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