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

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

このコミットは、Go言語の標準ライブラリである sort パッケージが math パッケージへの依存関係を持たないようにするための変更です。具体的には、float64 型のスライスをソートする際に必要となる NaN (Not a Number) の判定処理を、math パッケージの IsNaN 関数に依存するのではなく、sort パッケージ内部で同等のロジックを実装することで解決しています。これにより、sort パッケージの独立性が高まり、不要な依存関係が削減されます。

コミット

commit 4b091c533e2486b6d27e8f0754f0f2755f7931ff
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Sat Apr 20 17:20:41 2013 -0700

    sort: don't depend on math
    
    No reason to pull in math just for x != x.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/8886043

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

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

元コミット内容

sort: don't depend on math

No reason to pull in math just for x != x.

変更の背景

Go言語の sort パッケージは、様々な型のスライスをソートするための汎用的なインターフェースと実装を提供しています。float64 型のスライスをソートする際、浮動小数点数特有の NaN (Not a Number) 値の扱いが問題となります。IEEE 754 浮動小数点数標準では、NaN は自分自身と等しくないという特殊な性質を持っています (NaN != NaN が真となる)。

以前の sort パッケージの Float64SliceLess メソッドでは、この NaN の判定に math.IsNaN 関数を使用していました。しかし、math パッケージは数値計算に関する多くの機能を提供する比較的大きなパッケージであり、sort パッケージが単に NaN の判定のためだけに math パッケージ全体に依存することは、不必要な依存関係とみなされていました。

このコミットの背景には、Go言語の標準ライブラリにおけるモジュール間の依存関係を最小限に抑え、各パッケージの独立性を高めるという設計思想があります。sort パッケージが math パッケージに依存しないようにすることで、コンパイル時の依存関係が減り、潜在的な循環参照のリスクも低減されます。また、sort パッケージの利用者が math パッケージを必要としない場合に、その依存関係を意識する必要がなくなります。

前提知識の解説

浮動小数点数とNaN (Not a Number)

コンピュータにおける浮動小数点数は、実数を近似的に表現するための形式です。Go言語では float32float64 があります。IEEE 754 規格は、浮動小数点数の表現と演算に関する国際標準であり、多くのプログラミング言語やハードウェアで採用されています。

この規格には、通常の数値の他に、特殊な値が定義されています。その一つが NaN (Not a Number) です。NaN は、0による除算の結果や、無限大と無限大の減算など、数学的に未定義または表現不可能な演算の結果として生成されます。

NaN の最も重要な特性は、自分自身を含め、いかなる値とも等しくないという点です。つまり、NaN == NaNfalse と評価されます。この特性を利用して、ある浮動小数点数が NaN であるかどうかを判定することができます。具体的には、x != x という比較が true になる場合、xNaN であると判断できます。

Go言語の math パッケージと math.IsNaN

Go言語の標準ライブラリには、数学関数を提供する math パッケージがあります。このパッケージには、IsNaN(f float64) bool という関数が含まれており、引数 fNaN である場合に true を返します。この関数は内部的に f != f というロジックを使用しています。

Go言語の sort パッケージと sort.Interface

Go言語の sort パッケージは、任意のコレクションをソートするための機能を提供します。ソート可能なコレクションは、sort.Interface インターフェースを実装する必要があります。このインターフェースは以下の3つのメソッドを定義しています。

  • Len() int: コレクションの要素数を返します。
  • Less(i, j int) bool: インデックス i の要素がインデックス j の要素よりも小さい場合に true を返します。
  • Swap(i, j int): インデックス ij の要素を入れ替えます。

Float64Slice[]float64 型のスライスであり、この sort.Interface を実装しています。特に Less メソッドの実装において、NaN の特殊な比較ルールを考慮する必要があります。通常、NaN はソート順において他の数値よりも大きいとみなされるか、あるいは特定の順序を持たないものとして扱われます。Goの sort パッケージでは、NaN は非NaN の値よりも大きいと見なされ、複数の NaN が存在する場合はそれらの相対的な順序は保証されません。

技術的詳細

このコミットの技術的な核心は、sort パッケージが math パッケージへの依存を解消するために、math.IsNaN 関数のロジックを sort パッケージ内に直接コピーした点にあります。

変更前は、src/pkg/sort/sort.go 内の Float64SliceLess メソッドが以下のように定義されていました。

func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || math.IsNaN(p[i]) && !math.IsNaN(p[j]) }

この行は、p[i]p[j] より小さいか、または p[i]NaN であり、かつ p[j]NaN でない場合に true を返します。これにより、NaN は常に非NaN の値よりも大きいと判断され、ソートの際に末尾に配置されるようになります。

このコミットでは、math パッケージのインポートを削除し、代わりに isNaN というローカル関数を sort パッケージ内に定義しました。この isNaN 関数は、math.IsNaN と全く同じロジック、すなわち f != f を使用しています。

// isNaN is a copy of math.IsNaN to avoid a dependency on the math package.
func isNaN(f float64) bool {
	return f != f
}

そして、Float64SliceLess メソッドは、この新しく定義された isNaN 関数を使用するように変更されました。

func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || isNaN(p[i]) && !isNaN(p[j]) }

また、src/pkg/go/build/deps_test.go ファイルも更新され、sort パッケージが math パッケージに依存しないことを反映しています。pkgDeps マップは、Goのビルドシステムがパッケージ間の依存関係を追跡するために使用するものです。以前は sortmath に依存すると記述されていましたが、この変更によりその記述が削除されました。

--- a/src/pkg/go/build/deps_test.go
+++ b/src/pkg/go/build/deps_test.go
@@ -47,7 +47,7 @@ var pkgDeps = map[string][]string{\
 	"math":          {"unsafe"},\
 	"math/cmplx":    {"math"},\
 	"math/rand":     {"L0", "math"},\
-	"sort":          {"math"},\
+	"sort":          {},\
 	"strconv":       {"L0", "unicode/utf8", "math"},\
 	"unicode/utf16": {},\
 	"unicode/utf8":  {},\

この変更は、機能的な振る舞いを一切変えることなく、sort パッケージの依存関係をクリーンアップし、より自己完結型にするためのものです。

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

src/pkg/go/build/deps_test.go

--- a/src/pkg/go/build/deps_test.go
+++ b/src/pkg/go/build/deps_test.go
@@ -47,7 +47,7 @@ var pkgDeps = map[string][]string{\
 	"math":          {"unsafe"},\
 	"math/cmplx":    {"math"},\
 	"math/rand":     {"L0", "math"},\
-	"sort":          {"math"},\
+	"sort":          {},\
 	"strconv":       {\"L0\", \"unicode/utf8\", \"math\"},\
 	"unicode/utf16": {},\
 	"unicode/utf8":  {},\
  • pkgDeps マップから "sort": {"math"} のエントリが削除され、"sort": {} に変更されました。これは、sort パッケージが math パッケージに依存しなくなったことをビルドシステムに伝えるための変更です。

src/pkg/sort/sort.go

--- a/src/pkg/sort/sort.go
+++ b/src/pkg/sort/sort.go
@@ -6,8 +6,6 @@
 // collections.
 package sort
 
-import "math"
-
 // A type, typically a collection, that satisfies sort.Interface can be
 // sorted by the routines in this package.  The methods require that the
 // elements of the collection be enumerated by an integer index.\n@@ -245,9 +243,14 @@ func (p IntSlice) Sort() { Sort(p) }\n type Float64Slice []float64\n \n func (p Float64Slice) Len() int           { return len(p) }\n-func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || math.IsNaN(p[i]) && !math.IsNaN(p[j]) }\n+func (p Float64Slice) Less(i, j int) bool { return p[i] < p[j] || isNaN(p[i]) && !isNaN(p[j]) }\n func (p Float64Slice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }\n \n+// isNaN is a copy of math.IsNaN to avoid a dependency on the math package.\n+func isNaN(f float64) bool {\n+\treturn f != f\n+}\n+\n // Sort is a convenience method.\n func (p Float64Slice) Sort() { Sort(p) }\n \n```

-   `import "math"` の行が削除されました。
-   `Float64Slice` の `Less` メソッド内で `math.IsNaN` の代わりに `isNaN` 関数が呼び出されるように変更されました。
-   新しく `isNaN` というヘルパー関数が追加されました。この関数は `math.IsNaN` と同じロジック (`f != f`) を持ち、`float64` が `NaN` であるかを判定します。

## コアとなるコードの解説

このコミットの主要な目的は、`sort` パッケージから `math` パッケージへの依存を取り除くことです。

1.  **`math` パッケージのインポート削除**:
    `src/pkg/sort/sort.go` の冒頭にあった `import "math"` が削除されました。これにより、`sort` パッケージは `math` パッケージのコードをコンパイル時にリンクする必要がなくなります。

2.  **`isNaN` ヘルパー関数の導入**:
    `math.IsNaN` が提供していた `NaN` 判定機能は、浮動小数点数の特性 (`NaN != NaN` が真であること) を利用した非常にシンプルなものです。このため、`sort` パッケージ内に全く同じロジックを持つ `isNaN` 関数を新しく定義しました。
    ```go
    func isNaN(f float64) bool {
    	return f != f
    }
    ```
    この関数は、引数 `f` が `NaN` であれば `true` を返し、そうでなければ `false` を返します。

3.  **`Float64Slice.Less` メソッドの更新**:
    `Float64Slice` は `[]float64` 型のスライスをソートするための型です。その `Less` メソッドは、2つの要素 `p[i]` と `p[j]` の比較ロジックを定義します。
    変更前: `return p[i] < p[j] || math.IsNaN(p[i]) && !math.IsNaN(p[j])`
    変更後: `return p[i] < p[j] || isNaN(p[i]) && !isNaN(p[j])`
    この変更により、`math.IsNaN` の代わりに、`sort` パッケージ内部で定義された `isNaN` 関数が使用されるようになりました。これにより、`sort` パッケージは外部の `math` パッケージに依存することなく、`NaN` の比較を適切に処理できるようになります。

4.  **ビルド依存関係の更新**:
    `src/pkg/go/build/deps_test.go` は、Goのビルドシステムがパッケージ間の依存関係をテストするために使用するファイルです。このファイル内の `pkgDeps` マップは、各パッケージが依存するパッケージのリストを定義しています。
    以前は `"sort": {"math"}` と記述されており、`sort` パッケージが `math` パッケージに依存していることを示していました。このコミットにより、`sort` パッケージが `math` パッケージへの依存を解消したため、このエントリは `"sort": {}` に更新されました。これは、ビルドシステムが `sort` パッケージをビルドする際に `math` パッケージをリンクする必要がないことを意味します。

これらの変更により、`sort` パッケージは `NaN` の判定ロジックを自己完結させ、`math` パッケージへの不必要な依存を排除することに成功しました。これは、Go言語の標準ライブラリのモジュール性と効率性を向上させるための典型的な改善例と言えます。

## 関連リンク

-   Go言語 `sort` パッケージのドキュメント: [https://pkg.go.dev/sort](https://pkg.go.dev/sort)
-   Go言語 `math` パッケージのドキュメント: [https://pkg.go.dev/math](https://pkg.go.dev/math)
-   IEEE 754 浮動小数点数標準 (Wikipedia): [https://ja.wikipedia.org/wiki/IEEE_754%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0%E8%A6%8F%E6%A0%BC](https://ja.wikipedia.org/wiki/IEEE_754%E6%B5%AE%E5%8B%95%E5%B0%8F%E6%95%B0%E7%82%B9%E6%95%B0%E8%A6%8F%E6%A0%BC)
-   Go言語のコードレビューシステム (Gerrit): [https://go-review.googlesource.com/](https://go-review.googlesource.com/) (コミットメッセージにある `https://golang.org/cl/8886043` は、このGerritの変更リストへのリンクです。)

## 参考にした情報源リンク

-   コミット情報: `/home/orange/Project/comemo/commit_data/16208.txt`
-   Go言語のソースコード (GitHub): [https://github.com/golang/go](https://github.com/golang/go)
-   IEEE 754 浮動小数点数標準に関する一般的な知識
-   Go言語のパッケージ管理と依存関係に関する一般的な知識
-   Go言語の `sort` パッケージの内部実装に関する知識