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

[インデックス 1499] ファイルの概要

このコミットは、Go言語の標準ライブラリにおけるソート関連の関数およびテストユーティリティの命名規則を調整するものです。具体的には、いくつかの公開(エクスポートされた)関数や型、定数を非公開(エクスポートされていない)に変更し、Goの命名規則に沿った内部的な利用を意図した「casify」と呼ばれる変更が行われています。これにより、ライブラリの外部インターフェースがより明確になり、内部実装の詳細が隠蔽されます。

コミット

  • コミットハッシュ: 4b590bf985fc579e290f772cdd69c00f77d06a44
  • Author: Rob Pike r@golang.org
  • Date: Fri Jan 16 10:34:21 2009 -0800

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/4b590bf985fc579e290f772cdd69c00f77d06a44

元コミット内容

    casify a few more
    
    R=rsc
    DELTA=72  (0 added, 9 deleted, 63 changed)
    OCL=22918
    CL=22941

変更の背景

Go言語では、識別子(関数名、変数名、型名など)の最初の文字が大文字であるか小文字であるかによって、その識別子がパッケージ外に公開される(エクスポートされる)か、パッケージ内でのみ利用可能(非エクスポート)かが決まります。このコミットが行われた2009年1月は、Go言語がまだ初期開発段階にあり、言語仕様や標準ライブラリの設計が活発に行われていた時期です。

「casify」という表現は、このGo言語特有の命名規則に基づいて、識別子の公開範囲を調整する作業を指します。このコミットの背景には、以下のような目的があったと考えられます。

  1. APIの明確化: 外部に公開すべきでない内部ヘルパー関数や型を非公開にすることで、ライブラリの公開APIをより明確にし、利用者が混乱することなく必要な機能にアクセスできるようにする。
  2. カプセル化の強化: 内部実装の詳細を隠蔽し、ライブラリの安定性と保守性を向上させる。内部の変更が外部に影響を与えにくくなる。
  3. コードの一貫性: Goの命名規則に厳密に従い、コードベース全体で一貫したスタイルを維持する。

特に、ソートアルゴリズムのような基本的な機能を提供するライブラリでは、ユーザーが直接呼び出す必要のない内部的な補助関数が多く存在します。これらを非公開にすることで、ライブラリの利用者はsort.Sortのような主要な関数に集中でき、内部の複雑さに惑わされることがなくなります。

前提知識の解説

Go言語における識別子の可視性(Visibility)は、その識別子の最初の文字が大文字か小文字かによって決定されます。

  • エクスポートされた識別子 (Exported Identifiers): 識別子の最初の文字が大文字の場合、その識別子は定義されたパッケージの外部からアクセス可能です。これは、他のパッケージからインポートして利用できることを意味します。例えば、fmt.PrintlnPrintlnはエクスポートされた関数です。
  • 非エクスポートされた識別子 (Unexported Identifiers): 識別子の最初の文字が小文字の場合、その識別子は定義されたパッケージ内でのみアクセス可能です。パッケージの外部からは直接アクセスできません。これらは通常、パッケージの内部実装の詳細を構成するヘルパー関数、変数、型などに使用されます。

この規則は、Go言語が持つシンプルで強力なカプセル化のメカニズムの一つです。他の言語でよく見られるpublic, private, protectedといったキーワードはGoには存在せず、この命名規則がその役割を果たします。

また、Goのテストファイル(_test.goで終わるファイル)では、テスト関数はTestで始まり、その後に続く文字が大文字である必要があります(例: func TestMyFunction(t *testing.T))。これは、go testコマンドがこれらの関数を自動的に発見して実行するために必要な規則です。

技術的詳細

このコミットでは、主に以下のファイルに対して「casify」変更が適用されています。

  1. src/lib/sort.go:

    • ソートアルゴリズムの内部ヘルパー関数が非公開化されました。
      • InsertionSort -> insertionSort
      • MedianOfThree -> medianOfThree
      • SwapRange -> swapRange
      • Pivot -> doPivot (名前も変更され、より内部的な役割が明確化)
      • Quicksort -> quickSort
    • これらの関数は、sort.Sort関数によって内部的に利用されるため、外部に公開する必要がありません。非公開化することで、sortパッケージの公開APIが簡潔になります。
    • Sort関数自体は引き続きexport func Sortとして公開されており、これはユーザーがソート機能を利用するための主要なエントリポイントです。
  2. src/lib/sort_test.go:

    • テストユーティリティの命名規則が調整されました。
      • func BentleyMcIlroyTests() が削除されました。これはおそらく不要になったか、別の方法でテストが統合されたためと考えられます。
      • export func TestSortLargeRandom -> export func TestSortLarge_Random:テスト関数名が変更されましたが、Testプレフィックスと大文字始まりは維持されており、引き続きエクスポートされたテスト関数として機能します。これは、テストフレームワークがテストを検出するために必要です。
      • 定数グループ(Sawtooth, Randなど)が非公開化されました。
        • Sawtooth = iota -> _Sawtooth = iota
        • Rand -> _Rand
        • ...など、すべての関連定数に_プレフィックスが付けられ、小文字始まりになりました。これは、これらの定数がテスト内部でのみ使用されることを示します。
      • type TestingData -> type testingData:テストヘルパー構造体が非公開化されました。
      • func Lg -> func lg:テストヘルパー関数が非公開化されました。
      • func Min が削除されました。これはsort.go内のmin関数(既に非公開)が利用されるようになったためと考えられます。
  3. src/lib/testing.go:

    • テストフレームワーク自体の内部ヘルパー関数が非公開化されました。
      • func Tabify -> func tabify
      • func TRunner -> func tRunner
    • export type Test:興味深いことに、type Testexport type Testに変更され、エクスポートされるようになりました。これは、testingパッケージの外部からTest型を参照する必要が生じたため、あるいはテストの定義方法に関する初期の設計変更の一環である可能性があります。

全体として、このコミットはGo言語の命名規則を徹底し、パッケージの公開インターフェースと内部実装を明確に区別することを目的としています。これにより、コードの可読性、保守性、およびAPIの安定性が向上します。

コアとなるコードの変更箇所

このコミットのコアとなる変更は、主にGoの識別子の最初の文字を大文字から小文字に変更することで、その可視性をエクスポートから非エクスポートへと変更した点です。

src/lib/sort.go

--- a/src/lib/sort.go
+++ b/src/lib/sort.go
@@ -18,7 +18,7 @@ func min(a, b int) int {
 }
 
 // Insertion sort
-func InsertionSort(data SortInterface, a, b int) {
+func insertionSort(data SortInterface, a, b int) {
 	for i := a+1; i < b; i++ {
 		for j := i; j > a && data.Less(j, j-1); j-- {
 			data.Swap(j, j-1);
@@ -30,7 +30,7 @@ func InsertionSort(data SortInterface, a, b int) {
 // ``Engineering a Sort Function,''' SP&E November 1993.
 
 // Move the median of the three values data[a], data[b], data[c] into data[a].
-func MedianOfThree(data SortInterface, a, b, c int) {
+func medianOfThree(data SortInterface, a, b, c int) {
 	m0 := b;
 	m1 := a;
 	m2 := c;
@@ -41,22 +41,22 @@ func MedianOfThree(data SortInterface, a, b, c int) {
 	// now data[m0] <= data[m1] <= data[m2]
 }
 
-func SwapRange(data SortInterface, a, b, n int) {
+func swapRange(data SortInterface, a, b, n int) {
 	for i := 0; i < n; i++ {
 		data.Swap(a+i, b+i);
 	}
 }
 
-func Pivot(data SortInterface, lo, hi int) (midlo, midhi int) {
+func doPivot(data SortInterface, lo, hi int) (midlo, midhi int) {
 	m := (lo+hi)/2;
 	if hi - lo > 40 {
 		// Tukey's ``Ninther,'' median of three medians of three.
 		s := (hi - lo) / 8;
-		MedianOfThree(data, lo, lo+s, lo+2*s);
-		MedianOfThree(data, m, m-s, m+s);
-		MedianOfThree(data, hi-1, hi-1-s, hi-1-2*s);
+		medianOfThree(data, lo, lo+s, lo+2*s);
+		medianOfThree(data, m, m-s, m+s);
+		medianOfThree(data, hi-1, hi-1-s, hi-1-2*s);
 	}
-	MedianOfThree(data, lo, m, hi-1);
+	medianOfThree(data, lo, m, hi-1);
 
 	// Invariants are:
 	//	data[lo] = pivot (set up by ChoosePivot)
@@ -98,26 +98,26 @@ func Pivot(data SortInterface, lo, hi int) (midlo, midhi int) {
 	}
 
 	n := min(b-a, a-lo);
-	SwapRange(data, lo, b-n, n);
+	swapRange(data, lo, b-n, n);
 
 	n = min(hi-d, d-c);
-	SwapRange(data, c, hi-n, n);
+	swapRange(data, c, hi-n, n);
 
 	return lo+b-a, hi-(d-c);
 }
 
-func Quicksort(data SortInterface, a, b int) {
+func quickSort(data SortInterface, a, b int) {
 	if b - a > 7 {
-		mlo, mhi := Pivot(data, a, b);
-		Quicksort(data, a, mlo);
-		Quicksort(data, mhi, b);
+		mlo, mhi := doPivot(data, a, b);
+		quickSort(data, a, mlo);
+		quickSort(data, mhi, b);
 	} else if b - a > 1 {
-		InsertionSort(data, a, b);
+		insertionSort(data, a, b);
 	}
 }
 
 export func Sort(data SortInterface) {
-	Quicksort(data, 0, data.Len());
+	quickSort(data, 0, data.Len());
 }

src/lib/sort_test.go

--- a/src/lib/sort_test.go
+++ b/src/lib/sort_test.go
@@ -75,7 +73,7 @@ export func TestSortStrings(t *testing.T) {
 	}
 }
 
-export func TestSortLargeRandom(t *testing.T) {
+export func TestSortLarge_Random(t *testing.T) {
 	data := make([]int, 1000000);
 	for i := 0; i < len(data); i++ {
 		data[i] = rand.rand() % 100;
@@ -90,25 +88,25 @@ export func TestSortLargeRandom(t *testing.T) {
 }
 
 const (
-\tSawtooth = iota;
-\tRand;
-\tStagger;
-\tPlateau;
-\tShuffle;
-\tNDist;
+\t_Sawtooth = iota;
+\t_Rand;
+\t_Stagger;
+\t_Plateau;
+\t_Shuffle;
+\t_NDist;
 )
 
 const (
-\tCopy = iota;
-\tReverse;
-\tReverseFirstHalf;
-\tReverseSecondHalf;
-\tSorted;
-\tDither;
-\tNMode;
-);
-\n-type TestingData struct {
+\t_Copy = iota;
+\t_Reverse;
+\t_ReverseFirstHalf;
+\t_ReverseSecondHalf;
+\t_Sorted;
+\t_Dither;
+\t_NMode;
+)
+
+type testingData struct {
 \tdesc string;
 \tt *testing.T;
 \tdata []int;
@@ -116,9 +114,9 @@ type TestingData struct {
 \tnswap int;
 }
 
-func (d *TestingData) Len() int { return len(d.data); }
-func (d *TestingData) Less(i, j int) bool { return d.data[i] < d.data[j]; }
-func (d *TestingData) Swap(i, j int) {
+func (d *testingData) Len() int { return len(d.data); }
+func (d *testingData) Less(i, j int) bool { return d.data[i] < d.data[j]; }
+func (d *testingData) Swap(i, j int) {
 	if d.nswap >= d.maxswap {
 		d.t.Errorf("%s: used %d swaps sorting array of %d", d.desc, d.nswap, len(d.data));
 		d.t.FailNow();
@@ -127,13 +125,6 @@ func (d *TestingData) Swap(i, j int) {
 	d.data[i], d.data[j] = d.data[j], d.data[i];
 }
 
-func Lg(n int) int {
-	i := 0;
-	for 1<<uint(i) < n {
-		i++;
-	}
-	return i;
-}
-
-func Min(a, b int) int {
-	if a < b {
-		return a;
-	}
-	return b;
-}
-
 export func TestBentleyMcIlroy(t *testing.T) {
 	sizes := []int{100, 1023, 1024, 1025};
 	dists := []string{"sawtooth", "rand", "stagger", "plateau", "shuffle"};
@@ -141,21 +122,21 @@ export func TestBentleyMcIlroy(t *testing.T) {
 	for ni := 0; ni < len(sizes); ni++ {
 		n := sizes[ni];
 		for m := 1; m < 2*n; m *= 2 {
-\t\t\tfor dist := 0; dist < NDist; dist++ {
+\t\t\tfor dist := 0; dist < _NDist; dist++ {
 				j := 0;
 				k := 1;
 				data := tmp1[0:n];
 				for i := 0; i < n; i++ {
 					switch dist {
-\t\t\t\t\tcase Sawtooth:\
+\t\t\t\t\tcase _Sawtooth:\
 						data[i] = i % m;
-\t\t\t\t\tcase Rand:\
+\t\t\t\t\tcase _Rand:\
 						data[i] = rand.rand() % m;
-\t\t\t\t\tcase Stagger:\
+\t\t\t\t\tcase _Stagger:\
 						data[i] = (i*m + i) % n;
-\t\t\t\t\tcase Plateau:\
-\t\t\t\t\t\tdata[i] = Min(i, m);\
-\t\t\t\t\tcase Shuffle:\
+\t\t\t\t\tcase _Plateau:\
+\t\t\t\t\t\tdata[i] = min(i, m);\
+\t\t\t\t\tcase _Shuffle:\
 						if rand.rand() % m != 0 {
 							j += 2;
 							data[i] = j;
@@ -176,45 +167,45 @@ export func TestBentleyMcIlroy(t *testing.T) {
 				}
 
 				mdata := tmp2[0:n];
-\t\t\t\tfor mode := 0; mode < NMode; mode++ {
+\t\t\t\tfor mode := 0; mode < _NMode; mode++ {
 					switch mode {
-\t\t\t\t\tcase Copy:\
+\t\t\t\t\tcase _Copy:\
 						for i := 0; i < n; i++ {
 							mdata[i] = data[i];
 						}
-\t\t\t\t\tcase Reverse:\
+\t\t\t\t\tcase _Reverse:\
 						for i := 0; i < n; i++ {
 							mdata[i] = data[n-i-1];
 						}
-\t\t\t\t\tcase ReverseFirstHalf:\
+\t\t\t\t\tcase _ReverseFirstHalf:\
 						for i := 0; i < n/2; i++ {
 							mdata[i] = data[n/2-i-1];
 						}
 						for i := n/2; i < n; i++ {
 							mdata[i] = data[i];
 						}
-\t\t\t\t\tcase ReverseSecondHalf:\
+\t\t\t\t\tcase _ReverseSecondHalf:\
 						for i := 0; i < n/2; i++ {
 							mdata[i] = data[i];
 						}
 						for i := n/2; i < n; i++ {
 							mdata[i] = data[n-(i-n/2)-1];
 						}
-\t\t\t\t\tcase Sorted:\
+\t\t\t\t\tcase _Sorted:\
 						for i := 0; i < n; i++ {
 							mdata[i] = data[i];
 						}
 						// sort.SortInts is known to be correct
-\t\t\t\t\t\t// because mode Sort runs after mode Copy.
+\t\t\t\t\t\t// because mode Sort runs after mode _Copy.
 						sort.SortInts(mdata);
-\t\t\t\t\tcase Dither:\
+\t\t\t\t\tcase _Dither:\
 						for i := 0; i < n; i++ {
 							mdata[i] = data[i] + i%5;
 						}
 					}
 
 					desc := fmt.Sprintf("n=%d m=%d dist=%s mode=%s", n, m, dists[dist], modes[mode]);
-\t\t\t\t\td := &TestingData{desc, t, mdata[0:n], n*Lg(n)*12/10, 0};\
+\t\t\t\t\td := &testingData{desc, t, mdata[0:n], n*lg(n)*12/10, 0};\
 					sort.Sort(d);
 
 					// If we were testing C qsort, we'd have to make a copy

src/lib/testing.go

--- a/src/lib/testing.go
+++ b/src/lib/testing.go
@@ -12,10 +12,10 @@ import (
 var chatty = flag.Bool("chatty", false, "chatty")
 
 // Insert tabs after newlines - but not the last one
-func Tabify(s string) string {
+func tabify(s string) string {
 	for i := 0; i < len(s) - 1; i++ {	// -1 because if last char is newline, don't bother
 		if s[i] == '\n' {
-\t\t\treturn s[0:i+1] + "\t" + Tabify(s[i+1:len(s)]);
+\t\t\treturn s[0:i+1] + "\t" + tabify(s[i+1:len(s)]);
 		}
 	}
 	return s
@@ -38,11 +38,11 @@ func (t *T) FailNow() {
 }
 
 func (t *T) Log(args ...) {
-\tt.errors += "\t" + Tabify(fmt.Sprintln(args));
+\tt.errors += "\t" + tabify(fmt.Sprintln(args));
 }
 
 func (t *T) Logf(format string, args ...) {
-\tt.errors += Tabify(fmt.Sprintf("\t" + format, args));
+\tt.errors += tabify(fmt.Sprintf("\t" + format, args));
 	l := len(t.errors);
 	if l > 0 && t.errors[l-1] != '\n' {
 		t.errors += "\n"
@@ -74,7 +74,7 @@ export type Test struct {
 	f *(*T);
 }
 
-func TRunner(t *T, test *Test) {
+func tRunner(t *T, test *Test) {
 	test.f(t);
 	t.ch <- t;
 }
@@ -91,7 +91,7 @@ export func Main(tests []Test) {
 		}
 		t := new(T);
 		t.ch = make(chan *T);
-\t\tgo TRunner(t, &tests[i]);
+\t\tgo tRunner(t, &tests[i]);
 		<-t.ch;
 		if t.failed {
 			println("--- FAIL:", tests[i].name);

コアとなるコードの解説

このコミットにおける「casify」の主な目的は、Go言語の命名規則に基づき、パッケージの外部に公開すべきでない内部的なヘルパー関数、型、定数を非公開にすることです。

  • src/lib/sort.go の変更:

    • InsertionSort, MedianOfThree, SwapRange, Pivot, Quicksort といった関数は、ソートアルゴリズムの内部的なステップを実装するものです。これらはsort.Sort関数によって呼び出されるだけであり、パッケージの外部から直接アクセスされることを意図していません。したがって、これらの関数名を小文字始まりに変更することで、非公開化され、sortパッケージの公開APIがSort関数のみに絞り込まれました。これにより、ユーザーはsort.Sortだけを使えばよく、内部実装の詳細に煩わされることがなくなります。
    • PivotdoPivotに名前変更されたのは、単なる可視性の変更だけでなく、その役割がより明確に内部的な「ピボット処理を実行する」というニュアンスを伝えるためと考えられます。
  • src/lib/sort_test.go の変更:

    • テストコード内の定数(Sawtooth, Randなど)や型(TestingData)、ヘルパー関数(Lg)が非公開化されました。これらはテストの内部でのみ使用されるため、Goの命名規則に従って小文字始まりに変更されました。特に定数には_プレフィックスが付けられ、これはGoの慣習として、未使用のインポートや変数を示すためにも使われますが、ここでは内部的な定数であることを強調する意味合いが強いです。
    • TestSortLargeRandomTestSortLarge_Randomに変更されたのは、テスト関数としてのエクスポート状態を維持しつつ、命名の一貫性や可読性を向上させるための調整と考えられます。
    • Min関数の削除は、sort.goに既に存在するmin関数(非公開)を利用するように変更されたためです。これにより、コードの重複が排除され、一貫性が保たれます。
  • src/lib/testing.go の変更:

    • TabifyTRunnerといったtestingパッケージの内部ヘルパー関数が非公開化されました。これらはtestingパッケージの内部でテストの実行やログの整形を補助するものであり、外部に公開する必要はありません。
    • type Testexport type Testに変更されたのは、testingパッケージの外部からTest型を参照する必要が生じたため、あるいはテストの定義方法に関する初期の設計変更の一環である可能性があります。これは、このコミットの一般的な「casify」(非公開化)の傾向とは逆の変更であり、特定の設計上の理由があったことを示唆しています。

これらの変更は、Go言語の設計思想である「シンプルさ」と「明確さ」を反映しています。公開すべきものとそうでないものを明確に区別することで、ライブラリの利用者はより直感的にAPIを理解し、開発者は内部実装を自由に改善できるようになります。

関連リンク

  • Effective Go - Names: Go言語の命名規則に関する公式ドキュメント。エクスポートされた識別子と非エクスポートされた識別子について詳しく説明されています。
  • Go Modules - Go Language Specification: Go言語仕様におけるエクスポートされた識別子の定義。

参考にした情報源リンク