[インデックス 17501] ファイルの概要
このコミットは、Go言語の標準ライブラリ sort
パッケージにおける sort.Interface
の使用例を改善するものです。具体的には、よりシンプルで理解しやすい新しい例を追加し、既存の複雑な例を別のファイルに移動してリネームしています。これにより、sort.Interface
の基本的な使い方を学ぶ際の障壁が低減され、ドキュメントの可読性が向上しています。
コミット
commit 467122ce0b5f9daeb44b073362d53e772e321947
Author: Andriy Lytvynov <lytvynov.a.v@gmail.com>
Date: Sun Sep 8 11:17:23 2013 +1000
sort: add a simpler sort.Interface example
Existing example renamed to Example_sortWrapper.
Fixes #6335.
R=golang-dev, rsc, taj.khattra, r
CC=golang-dev
https://golang.org/cl/13586043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/467122ce0b5f9daeb44b073362d53e772e321947
元コミット内容
sort: add a simpler sort.Interface example
Existing example renamed to Example_sortWrapper.
Fixes #6335.
変更の背景
このコミットの背景には、Go言語の sort
パッケージにおける sort.Interface
の使用例が、初心者にとって理解しにくいという問題がありました。元の ExampleInterface
は、sort.Interface
を埋め込んだ構造体(ByName
や ByWeight
)を使って複数のソート基準を実装するという、やや高度なテクニックを示していました。これは柔軟性を示す良い例ではありましたが、sort.Interface
の最も基本的な使い方を学ぶ上では複雑すぎると考えられました。
GoのIssue #6335("sort: add a simpler sort.Interface example")でこの点が指摘され、より単純な例の追加が求められました。このコミットは、その要望に応える形で、sort.Interface
の3つのメソッド(Len
, Swap
, Less
)を直接実装する、より簡潔な例を提供することで、学習曲線とドキュメントのアクセシビリティを改善することを目的としています。既存の複雑な例は削除されるのではなく、Example_sortWrapper
として別のテストファイルに移動され、引き続き参照できるように配慮されています。
前提知識の解説
Go言語の sort
パッケージ
Go言語の標準ライブラリ sort
パッケージは、スライスやユーザー定義のコレクションをソートするための機能を提供します。このパッケージは、特定のデータ型に特化したソート関数を提供するのではなく、汎用的なインターフェース sort.Interface
を介してソート機能を提供します。
sort.Interface
sort.Interface
は、Go言語でカスタム型をソート可能にするためのインターフェースです。このインターフェースは以下の3つのメソッドを定義しています。
Len() int
: ソート対象のコレクションの要素数を返します。Swap(i, j int)
: コレクション内のインデックスi
とj
の要素を入れ替えます。Less(i, j int) bool
: インデックスi
の要素がインデックスj
の要素よりも小さい(ソート順で前にある)場合にtrue
を返します。
任意のカスタム型がこれらの3つのメソッドを実装することで、その型は sort.Interface
を満たし、sort.Sort()
関数を使用してソートできるようになります。この設計は、Goのインターフェースの強力な例であり、型に依存しない汎用的なソートアルゴリズムを可能にしています。
Go言語のテストとExample関数
Go言語では、_test.go
で終わるファイルにテストコードを記述します。Example
というプレフィックスを持つ関数は、特別な種類のテスト関数であり、その出力がコメントとして記述された期待される出力と一致するかどうかを検証します。これらの Example
関数は、Goのドキュメント生成ツール godoc
によって自動的に抽出され、パッケージのドキュメントにコード例として表示されます。これにより、ユーザーはコードの動作を実際に確認しながら学習することができます。
技術的詳細
このコミットの技術的な変更は、主に sort.Interface
の使用例の再構成にあります。
-
src/pkg/sort/example_interface_test.go
の変更:- 元の
Organ
やGrams
といった複雑な構造体と、ByName
,ByWeight
のようなsort.Interface
を埋め込んだソートヘルパー構造体を使った例が削除されました。 - 代わりに、
Person
というシンプルな構造体(Name
とAge
フィールドを持つ)と、ByAge
という[]*Person
型を直接sort.Interface
に適合させる新しい例が導入されました。 ByAge
はLen()
,Swap()
,Less()
メソッドを直接実装し、Person
スライスを年齢でソートするシンプルなロジックを示しています。- この新しい
ExampleInterface
は、sort.Sort(ByAge(people))
のように、カスタム型をsort.Interface
にキャストしてソートする最も基本的なパターンを明確に示しています。
- 元の
-
src/pkg/sort/example_wrapper_test.go
の新規作成:- 元の
example_interface_test.go
にあったOrgan
関連の複雑な例(ByName
,ByWeight
を使用したもの)は、この新しいファイルにExample_sortWrapper
として移動されました。 - これにより、
sort.Interface
を直接実装するシンプルな例と、インターフェースをラップして複数のソート基準を扱うより高度な例が分離され、それぞれの目的が明確になりました。 Example_sortWrapper
という名前は、sort.Interface
をラップする(wrapper)パターンを示していることを明確にしています。
- 元の
この変更により、sort
パッケージのドキュメントを見た際に、まず最も基本的な sort.Interface
の実装方法が提示され、その後に必要に応じてより高度なパターン(ラッパー構造体)を参照できるようになりました。これは、ドキュメントの段階的な学習パスを改善し、特にGo言語やソートの概念に不慣れな開発者にとって大きな助けとなります。
コアとなるコードの変更箇所
src/pkg/sort/example_interface_test.go
--- a/src/pkg/sort/example_interface_test.go
+++ b/src/pkg/sort/example_interface_test.go
@@ -9,69 +9,35 @@ import (
"sort"
)
-type Grams int
-
-func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) }
-
-type Organ struct {
- Name string
- Weight Grams
+type Person struct {
+ Name string
+ Age int
}
-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 }
+func (p Person) String() string {
+ return fmt.Sprintf("%s: %d", p.Name, p.Age)
+}
-// ByWeight implements sort.Interface by providing Less and using the Len and
-// Swap methods of the embedded Organs value.\n-func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight }\n+type ByAge []*Person
-func ExampleInterface() {
-\ts := []*Organ{\n-\t\t{"brain", 1340},\n-\t\t{"heart", 290},\n-\t\t{"liver", 1494},\n-\t\t{"pancreas", 131},\n-\t\t{"prostate", 62},\n-\t\t{"spleen", 162},\n+\tfunc (a ByAge) Len() int { return len(a) }\n+\tfunc (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }\n+\tfunc (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }\n
-\tsort.Sort(ByWeight{s})\n-\tfmt.Println("Organs by weight:")\n-\tprintOrgans(s)\n-\n-\tsort.Sort(ByName{s})\n-\tfmt.Println("Organs by name:")\n-\tprintOrgans(s)\n+\tfunc ExampleInterface() {\n+\tpeople := []*Person{\n+\t\t&Person{Name: "Bob", Age: 31},\n+\t\t&Person{Name: "John", Age: 42},\n+\t\t&Person{Name: "Michael", Age: 17},\n+\t\t&Person{Name: "Jenny", Age: 26},\n \t}\n \n-\t// Output:\n-\t// Organs by weight:\n-\t// prostate (62g)\n-\t// pancreas (131g)\n-\t// spleen (162g)\n-\t// heart (290g)\n-\t// brain (1340g)\n-\t// liver (1494g)\n-\t// Organs by name:\n-\t// brain (1340g)\n-\t// heart (290g)\n-\t// liver (1494g)\n-\t// pancreas (131g)\n-\t// prostate (62g)\n-\t// spleen (162g)\n-}\n-\n-func printOrgans(s []*Organ) {\n-\tfor _, o := range s {\n-\t\tfmt.Printf("%-8s (%v)\\n", o.Name, o.Weight)\n-\t}\n+\tfmt.Println(people)\n+\tsort.Sort(ByAge(people))\n+\tfmt.Println(people)\n \n+\t// Output: [Bob: 31 John: 42 Michael: 17 Jenny: 26]\n+\t// [Michael: 17 Jenny: 26 Bob: 31 John: 42]\n }
src/pkg/sort/example_wrapper_test.go
(新規ファイル)
--- /dev/null
+++ b/src/pkg/sort/example_wrapper_test.go
@@ -0,0 +1,77 @@
+// 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 Example_sortWrapper() {
+ 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_interface_test.go
の変更点
このファイルでは、ExampleInterface
関数が大幅に簡素化されました。
Person
構造体の導入:Name
(string) とAge
(int) という非常にシンプルなフィールドを持つPerson
構造体が定義されました。これは、ソート対象のデータ構造として、より一般的で理解しやすいものです。ByAge
型の導入とsort.Interface
の直接実装:type ByAge []*Person
と定義され、[]*Person
型のスライスをソートするための型エイリアスとして機能します。- この
ByAge
型に対して、Len()
,Swap()
,Less()
の3つのメソッドが直接実装されています。Len()
: スライスの長さを返します。Swap(i, j int)
: スライスのi
番目とj
番目の要素を入れ替えます。Less(i, j int) bool
:i
番目のPerson
のAge
がj
番目のPerson
のAge
より小さい場合にtrue
を返します。これにより、年齢の昇順でソートされます。
ExampleInterface
関数の簡素化:Person
のスライスを作成し、sort.Sort(ByAge(people))
を呼び出すことで、年齢順にソートされる様子を示しています。- 出力も非常にシンプルになり、ソート前後のスライスの内容が直接表示されます。
この変更により、sort.Interface
を実装する最も基本的なパターン(ソートしたいスライス型に対して直接 Len
, Swap
, Less
メソッドを定義する)が明確に示され、Goのソート機能の入門として最適化されました。
src/pkg/sort/example_wrapper_test.go
の新規作成と内容
このファイルは、元の example_interface_test.go
にあった、より複雑なソート例を保持するために新しく作成されました。
Grams
,Organ
,Organs
型の定義: これらは元の例で使用されていた型で、臓器の重さや名前を表現しています。ByName
,ByWeight
ラッパー構造体の定義:type ByName struct{ Organs }
やtype ByWeight struct{ Organs }
のように、Organs
スライスを埋め込んだ構造体として定義されています。- これらの構造体は、埋め込まれた
Organs
のLen()
とSwap()
メソッドを再利用しつつ、独自のLess()
メソッドを実装することで、名前順や重さ順といった異なるソート基準を提供します。 - このパターンは、同じ基底データに対して複数のソート基準を提供したい場合に有用です。
Example_sortWrapper
関数の定義:- この関数は、
Organ
のスライスを作成し、sort.Sort(ByWeight{s})
やsort.Sort(ByName{s})
のように、ラッパー構造体を使用してソートを実行する様子を示しています。 - 出力も元の例と同じく、ソートされた臓器のリストが表示されます。
- この関数は、
このファイルは、sort.Interface
のより高度な使用パターン、特に「ラッパー」パターンを示すための独立した例として機能します。これにより、基本的なソートの概念を学んだ後に、より複雑なソート要件に対応する方法を学ぶことができます。
関連リンク
- Go Issue #6335: https://github.com/golang/go/issues/6335
- Go CL 13586043: https://golang.org/cl/13586043
参考にした情報源リンク
- Go言語
sort
パッケージ公式ドキュメント: https://pkg.go.dev/sort - Go言語の
Example
関数に関するドキュメント: https://go.dev/blog/examples - Go言語のインターフェースに関する公式ドキュメント: https://go.dev/tour/methods/10 (Go Tourの該当セクションなど)
- Go言語の
fmt
パッケージ公式ドキュメント: https://pkg.go.dev/fmt (fmt.Sprintf
やfmt.Println
の理解のため) - Go言語の構造体とメソッドに関する公式ドキュメント: https://go.dev/tour/methods/1 (Go Tourの該当セクションなど)
- Go言語のスライスに関する公式ドキュメント: https://go.dev/tour/moretypes/7 (Go Tourの該当セクションなど)
- Go言語のポインタに関する公式ドキュメント: https://go.dev/tour/moretypes/1 (Go Tourの該当セクションなど)
- Go言語の埋め込み(Embedding)に関するドキュメント: https://go.dev/tour/methods/15 (Go Tourの該当セクションなど)
- Go言語のテストに関する公式ドキュメント: https://pkg.go.dev/testing