Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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 を埋め込んだ構造体(ByNameByWeight)を使って複数のソート基準を実装するという、やや高度なテクニックを示していました。これは柔軟性を示す良い例ではありましたが、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つのメソッドを定義しています。

  1. Len() int: ソート対象のコレクションの要素数を返します。
  2. Swap(i, j int): コレクション内のインデックス ij の要素を入れ替えます。
  3. 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 の使用例の再構成にあります。

  1. src/pkg/sort/example_interface_test.go の変更:

    • 元の OrganGrams といった複雑な構造体と、ByName, ByWeight のような sort.Interface を埋め込んだソートヘルパー構造体を使った例が削除されました。
    • 代わりに、Person というシンプルな構造体(NameAge フィールドを持つ)と、ByAge という []*Person 型を直接 sort.Interface に適合させる新しい例が導入されました。
    • ByAgeLen(), Swap(), Less() メソッドを直接実装し、Person スライスを年齢でソートするシンプルなロジックを示しています。
    • この新しい ExampleInterface は、sort.Sort(ByAge(people)) のように、カスタム型を sort.Interface にキャストしてソートする最も基本的なパターンを明確に示しています。
  2. 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 番目の PersonAgej 番目の PersonAge より小さい場合に 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 スライスを埋め込んだ構造体として定義されています。
    • これらの構造体は、埋め込まれた OrgansLen()Swap() メソッドを再利用しつつ、独自の Less() メソッドを実装することで、名前順や重さ順といった異なるソート基準を提供します。
    • このパターンは、同じ基底データに対して複数のソート基準を提供したい場合に有用です。
  • Example_sortWrapper 関数の定義:
    • この関数は、Organ のスライスを作成し、sort.Sort(ByWeight{s})sort.Sort(ByName{s}) のように、ラッパー構造体を使用してソートを実行する様子を示しています。
    • 出力も元の例と同じく、ソートされた臓器のリストが表示されます。

このファイルは、sort.Interface のより高度な使用パターン、特に「ラッパー」パターンを示すための独立した例として機能します。これにより、基本的なソートの概念を学んだ後に、より複雑なソート要件に対応する方法を学ぶことができます。

関連リンク

参考にした情報源リンク