[インデックス 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
パッケージの Float64Slice
の Less
メソッドでは、この NaN
の判定に math.IsNaN
関数を使用していました。しかし、math
パッケージは数値計算に関する多くの機能を提供する比較的大きなパッケージであり、sort
パッケージが単に NaN
の判定のためだけに math
パッケージ全体に依存することは、不必要な依存関係とみなされていました。
このコミットの背景には、Go言語の標準ライブラリにおけるモジュール間の依存関係を最小限に抑え、各パッケージの独立性を高めるという設計思想があります。sort
パッケージが math
パッケージに依存しないようにすることで、コンパイル時の依存関係が減り、潜在的な循環参照のリスクも低減されます。また、sort
パッケージの利用者が math
パッケージを必要としない場合に、その依存関係を意識する必要がなくなります。
前提知識の解説
浮動小数点数とNaN (Not a Number)
コンピュータにおける浮動小数点数は、実数を近似的に表現するための形式です。Go言語では float32
と float64
があります。IEEE 754 規格は、浮動小数点数の表現と演算に関する国際標準であり、多くのプログラミング言語やハードウェアで採用されています。
この規格には、通常の数値の他に、特殊な値が定義されています。その一つが NaN
(Not a Number) です。NaN
は、0による除算の結果や、無限大と無限大の減算など、数学的に未定義または表現不可能な演算の結果として生成されます。
NaN
の最も重要な特性は、自分自身を含め、いかなる値とも等しくないという点です。つまり、NaN == NaN
は false
と評価されます。この特性を利用して、ある浮動小数点数が NaN
であるかどうかを判定することができます。具体的には、x != x
という比較が true
になる場合、x
は NaN
であると判断できます。
Go言語の math
パッケージと math.IsNaN
Go言語の標準ライブラリには、数学関数を提供する math
パッケージがあります。このパッケージには、IsNaN(f float64) bool
という関数が含まれており、引数 f
が NaN
である場合に 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)
: インデックスi
とj
の要素を入れ替えます。
Float64Slice
は []float64
型のスライスであり、この sort.Interface
を実装しています。特に Less
メソッドの実装において、NaN
の特殊な比較ルールを考慮する必要があります。通常、NaN
はソート順において他の数値よりも大きいとみなされるか、あるいは特定の順序を持たないものとして扱われます。Goの sort
パッケージでは、NaN
は非NaN
の値よりも大きいと見なされ、複数の NaN
が存在する場合はそれらの相対的な順序は保証されません。
技術的詳細
このコミットの技術的な核心は、sort
パッケージが math
パッケージへの依存を解消するために、math.IsNaN
関数のロジックを sort
パッケージ内に直接コピーした点にあります。
変更前は、src/pkg/sort/sort.go
内の Float64Slice
の Less
メソッドが以下のように定義されていました。
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
}
そして、Float64Slice
の Less
メソッドは、この新しく定義された 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のビルドシステムがパッケージ間の依存関係を追跡するために使用するものです。以前は sort
が math
に依存すると記述されていましたが、この変更によりその記述が削除されました。
--- 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` パッケージの内部実装に関する知識