[インデックス 11596] ファイルの概要
このコミットは、Go言語の標準ライブラリであるmath/big
パッケージにおける2つの主要な改善を含んでいます。一つは、パッケージコメントの記述をより正確にし、メソッドのレシーバと戻り値に関する慣習を明確にすることです。もう一つは、math/big/rat.go
ファイル内のいくつかのメソッドのレシーバ名をz
からx
に変更し、コードベース全体の一貫性を向上させることです。
コミット
commit d0607221faec743a726cb38e1dd12b337c33a775
Author: Robert Griesemer <gri@golang.org>
Date: Fri Feb 3 10:17:19 2012 -0800
math/big: more accurate package comment
Fix some receiver names for consistency.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5624043
---
src/pkg/math/big/nat.go | 12 +++++++++---\n src/pkg/math/big/rat.go | 42 +++++++++++++++++++++---------------------\n 2 files changed, 30 insertions(+), 24 deletions(-)\n
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d0607221faec743a726cb38e1dd12b337c33a775
元コミット内容
math/big: more accurate package comment
Fix some receiver names for consistency.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5624043
変更の背景
このコミットは、Go言語の標準ライブラリの品質と一貫性を向上させるためのものです。
-
ドキュメンテーションの精度向上:
math/big
パッケージは、任意精度の数値演算を提供する重要なパッケージです。このような基盤となるパッケージのドキュメンテーションは、開発者が正しく、効率的にライブラリを使用するために極めて重要です。以前のパッケージコメントは、メソッドのレシーバと戻り値に関する慣習について、やや一般的な記述に留まっていました。この変更により、特に*Int
や*Rat
型を扱うメソッドの典型的なシグネチャと、それらがどのようにチェイン操作を可能にするか、そして結果以外の型を返すメソッドのレシーバの扱いについて、より具体的で正確な情報が提供されるようになりました。これにより、ユーザーはパッケージの設計思想と使用方法をより深く理解できるようになります。 -
コードの一貫性の確保: Go言語では、メソッドのレシーバ名に特定の慣習があります。特に、二項演算(例:
z = x Op y
)ではz
を結果、x
とy
をオペランドとして使用することが一般的です。しかし、String()
やGobEncode()
のように、演算結果を生成するのではなく、レシーバ自身の状態を表現したり、エンコードしたりするメソッドの場合、z
というレシーバ名は必ずしも適切ではありません。このコミットでは、これらのメソッドのレシーバ名をz
からx
に変更することで、math/big
パッケージ内のレシーバ名の使用法をより一貫性のあるものにし、コードの可読性とGo言語のイディオムへの準拠を向上させています。これは、大規模なコードベースにおいて、開発者がコードの意図を素早く理解し、誤解を避ける上で非常に重要です。
前提知識の解説
このコミットを理解するためには、以下のGo言語の概念とmath/big
パッケージに関する知識が役立ちます。
-
Go言語のレシーバ(Receiver): Go言語では、関数を型に関連付けてメソッドを定義します。このとき、メソッドが操作するインスタンスは「レシーバ」として指定されます。レシーバは、関数名の前に括弧で囲んで宣言されます(例:
func (r Type) MethodName(...)
)。レシーバは値型でもポインタ型でも指定できますが、通常、メソッドがレシーバの状態を変更する場合や、大きな構造体をコピーするコストを避けるためにポインタレシーバが使用されます。math/big
パッケージのメソッドは、ほとんどがポインタレシーバを使用しており、これは任意精度の数値型が通常、比較的大きなメモリフットプリントを持つためです。 -
Go言語におけるレシーバ名の慣習: Go言語のコミュニティでは、レシーバ名に短い単一文字を使用する慣習があります。特に、以下のような慣習が広く用いられています。
x
,y
: 演算のオペランド(入力値)を表す場合。z
: 演算の結果(出力値)を表す場合。特に、z = x Op y
のような形式のメソッドでよく使われます。r
: レシーバ自身を表す一般的な場合。s
: 文字列(string)に関連する場合。 このコミットでは、z
が結果を表すという慣習から外れるメソッド(例:String()
)において、レシーバ名をx
に変更することで、より適切な慣習に合わせようとしています。
-
math/big
パッケージ:math/big
パッケージは、Go言語で任意精度の算術演算を可能にするための型を提供します。これは、標準のGoの組み込み型(int
,float64
など)が持つ固定の精度制限を超える計算が必要な場合に利用されます。Int
: 任意精度の符号付き整数を扱います。Rat
: 任意精度の有理数(分数)を扱います。これは、分子と分母がそれぞれInt
型で表現されます。 このパッケージのメソッドは、通常、結果をレシーバとして受け取り、そのレシーバを返すことで、メソッドチェインを可能にする設計になっています。
-
gob
パッケージ:gob
パッケージは、Goのプログラム間でGoの値をエンコードおよびデコードするためのデータ形式を提供します。これは、ネットワーク経由でのデータ転送や、ファイルへの永続化などに使用されます。GobEncode
メソッドは、gob.GobEncoder
インターフェースの一部であり、型が自身をgob
形式でエンコードする方法を定義するために実装されます。
技術的詳細
このコミットは、src/pkg/math/big/nat.go
とsrc/pkg/math/big/rat.go
の2つのファイルに影響を与えています。
src/pkg/math/big/nat.go
におけるパッケージコメントの変更
このファイルでは、math/big
パッケージの冒頭にあるコメントが更新されました。変更の目的は、パッケージ内のメソッドのシグネチャに関する慣習をより明確にすることです。
変更前:
// All methods on Int take the result as the receiver; if it is one
// of the operands it may be overwritten (and its memory reused).
// To enable chaining of operations, the result is also returned.
この記述は、Int
型(および暗黙的にRat
型)のメソッドが結果をレシーバとして受け取り、チェイン操作のためにその結果を返すという一般的なパターンを説明しています。
変更後:
// Methods are typically of the form:
//
// func (z *Int) Op(x, y *Int) *Int (similar for *Rat)
//
// and implement operations z = x Op y with the result as receiver; if it
// is one of the operands it may be overwritten (and its memory reused).\n
// To enable chaining of operations, the result is also returned. Methods
// returning a result other than *Int or *Rat take one of the operands as
// the receiver.
新しいコメントは、以下の点を明確にしています。
- 典型的なシグネチャの例示:
func (z *Int) Op(x, y *Int) *Int
という具体的な形式を提示し、z
が結果、x
とy
がオペランドであることを示しています。これは*Rat
型にも同様に適用されます。 - 結果レシーバの役割:
z = x Op y
のような演算をレシーバとして実装し、オペランドの一つがレシーバと同じである場合にメモリを再利用できることを強調しています。 - チェイン操作のサポート: 結果が返されることで、
a.Add(b).Mul(c)
のようなメソッドチェインが可能になることを再確認しています。 - 非
*Int
/*Rat
戻り値のメソッド: 最も重要な追加点は、「*Int
または*Rat
以外の結果を返すメソッドは、オペランドの一つをレシーバとして取る」という新しい慣習を明記したことです。これは、例えばCmp(x *Int) int
のような比較メソッドや、String() string
のような文字列変換メソッドなど、結果が数値型ではない場合に、レシーバが演算結果ではなく、主要な入力オペランドとして扱われることを示唆しています。この明確化により、開発者はパッケージの設計原則をより深く理解し、適切なレシーバ名の選択に役立てることができます。
src/pkg/math/big/rat.go
におけるレシーバ名の変更
このファイルでは、Rat
型のいくつかのメソッドのレシーバ名がz
からx
に変更されました。対象となったメソッドは以下の通りです。
String() string
RatString() string
FloatString(prec int) string
GobEncode() ([]byte, error)
これらのメソッドは、いずれもRat
型のインスタンスを操作して、その文字列表現を返したり、gob
形式にエンコードしたりするものです。これらは、Add
やMul
のような二項演算のように「結果z
を生成する」という性質のものではありません。むしろ、レシーバ自身(x
)を主たる対象として処理を行います。
Go言語の慣習では、z
は通常、演算の結果を表すために使用されます。しかし、これらのメソッドは結果を生成するのではなく、レシーバ自身のプロパティを表現するため、x
というレシーバ名の方がより適切であると判断されました。この変更により、math/big
パッケージ全体でのレシーバ名の使用法に一貫性がもたらされ、コードの意図がより明確になります。
例えば、func (z *Rat) String() string
がfunc (x *Rat) String() string
に変更され、それに伴いメソッド内部のz
への参照がすべてx
に置き換えられています。これは純粋なリファクタリングであり、機能的な変更はありませんが、コードの可読性と保守性を向上させます。
コアとなるコードの変更箇所
src/pkg/math/big/nat.go
--- a/src/pkg/math/big/nat.go
+++ b/src/pkg/math/big/nat.go
@@ -8,9 +8,15 @@
// - Int signed integers
// - Rat rational numbers
//
-// All methods on Int take the result as the receiver; if it is one
-// of the operands it may be overwritten (and its memory reused).\n
-// To enable chaining of operations, the result is also returned.
+// Methods are typically of the form:
+//
+// func (z *Int) Op(x, y *Int) *Int (similar for *Rat)
+//
+// and implement operations z = x Op y with the result as receiver; if it
+// is one of the operands it may be overwritten (and its memory reused).
+// To enable chaining of operations, the result is also returned. Methods
+// returning a result other than *Int or *Rat take one of the operands as
+// the receiver.
//
package big
src/pkg/math/big/rat.go
--- a/src/pkg/math/big/rat.go
+++ b/src/pkg/math/big/rat.go
@@ -328,36 +328,36 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
}
// String returns a string representation of z in the form "a/b" (even if b == 1).
-func (z *Rat) String() string {
+func (x *Rat) String() string {
s := "/1"
- if len(z.b) != 0 {
- s = "/" + z.b.decimalString()
+ if len(x.b) != 0 {
+ s = "/" + x.b.decimalString()
}
- return z.a.String() + s
+ return x.a.String() + s
}
// RatString returns a string representation of z in the form "a/b" if b != 1,
// and in the form "a" if b == 1.
-func (z *Rat) RatString() string {
- if z.IsInt() {
- return z.a.String()
+func (x *Rat) RatString() string {
+ if x.IsInt() {
+ return x.a.String()
}
- return z.String()
+ return x.String()
}
// FloatString returns a string representation of z in decimal form with prec
// digits of precision after the decimal point and the last digit rounded.
-func (z *Rat) FloatString(prec int) string {
- if z.IsInt() {
- s := z.a.String()
+func (x *Rat) FloatString(prec int) string {
+ if x.IsInt() {
+ s := x.a.String()
if prec > 0 {
s += "." + strings.Repeat("0", prec)
}
return s
}
- // z.b != 0
+ // x.b != 0
- q, r := nat(nil).div(nat(nil), z.a.abs, z.b)
+ q, r := nat(nil).div(nat(nil), x.a.abs, x.b)
p := natOne
if prec > 0 {
@@ -365,11 +365,11 @@ func (z *Rat) FloatString(prec int) string {
}
r = r.mul(r, p)
- r, r2 := r.div(nat(nil), r, z.b)
+ r, r2 := r.div(nat(nil), r, x.b)
// see if we need to round up
r2 = r2.add(r2, r2)
- if z.b.cmp(r2) <= 0 {
+ if x.b.cmp(r2) <= 0 {
r = r.add(r, natOne)
if r.cmp(p) >= 0 {
q = nat(nil).add(q, natOne)
@@ -378,7 +378,7 @@ func (z *Rat) FloatString(prec int) string {
}
s := q.decimalString()
- if z.a.neg {
+ if x.a.neg {
s = "-" + s
}
@@ -395,10 +395,10 @@ const ratGobVersion byte = 1
// GobEncode implements the gob.GobEncoder interface.
-func (z *Rat) GobEncode() ([]byte, error) {
- buf := make([]byte, 1+4+(len(z.a.abs)+len(z.b))*_S) // extra bytes for version and sign bit (1), and numerator length (4)
- i := z.b.bytes(buf)
- j := z.a.abs.bytes(buf[0:i])
+func (x *Rat) GobEncode() ([]byte, error) {
+ buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b))*_S) // extra bytes for version and sign bit (1), and numerator length (4)
+ i := x.b.bytes(buf)
+ j := x.a.abs.bytes(buf[0:i])
n := i - j
if int(uint32(n)) != n {
// this should never happen
@@ -407,7 +407,7 @@ func (z *Rat) GobEncode() ([]byte, error) {
binary.BigEndian.PutUint32(buf[j-4:j], uint32(n))
j -= 1 + 4
b := ratGobVersion << 1 // make space for sign bit
- if z.a.neg {
+ if x.a.neg {
b |= 1
}
buf[j] = b
コアとなるコードの解説
src/pkg/math/big/nat.go
のコメント変更
この変更は、math/big
パッケージのドキュメンテーションを改善するものです。特に、Go言語におけるメソッドのレシーバの役割と、math/big
パッケージが採用している特定の慣習を明確にしています。
func (z *Int) Op(x, y *Int) *Int
の導入: これは、math/big
パッケージにおける典型的な二項演算メソッドのシグネチャを示しています。ここで、z
は演算結果が格納されるレシーバであり、x
とy
は入力オペランドです。メソッドが*Int
(または*Rat
)を返すことで、result.Add(a, b).Mul(c, d)
のように複数の演算をチェインして記述することが可能になります。- 「
*Int
または*Rat
以外の結果を返すメソッドは、オペランドの一つをレシーバとして取る」: この新しい記述は非常に重要です。例えば、Cmp(x *Int) int
のような比較メソッドは、結果としてint
(-1, 0, 1)を返します。このようなメソッドでは、レシーバは演算結果を格納するz
ではなく、比較の対象となる主要なオペランドとして機能します。この場合、レシーバ名としてx
がより適切であるという慣習を示唆しています。この明確化により、開発者はmath/big
パッケージのメソッドシグネチャの背後にある設計原則をより深く理解し、自身のコードで同様のパターンを適用する際の指針とすることができます。
src/pkg/math/big/rat.go
のレシーバ名変更
この変更は、Rat
型のString()
, RatString()
, FloatString()
, GobEncode()
メソッドのレシーバ名をz
からx
に統一するものです。
-
z
からx
への変更の理由: Go言語の慣習では、z
は通常、演算の結果を格納するレシーバとして使用されます。しかし、これらのメソッドは演算結果を生成するものではありません。String()
,RatString()
,FloatString()
: これらはRat
型のインスタンスの文字列表現を返します。レシーバは、その文字列表現を生成する「対象」であり、演算の「結果」ではありません。GobEncode()
: これはRat
型のインスタンスをgob
形式にエンコードします。ここでも、レシーバはエンコードされる「対象」であり、演算の結果ではありません。 したがって、これらのメソッドにおいてレシーバをx
と命名することは、そのレシーバが「主たるオペランド」または「操作の対象」であることをより適切に表現し、Go言語のイディオムに沿ったものとなります。
-
コードへの影響: この変更は純粋なリファクタリングであり、メソッドの機能的な振る舞いには一切影響を与えません。しかし、コードベース全体でレシーバ名の命名規則に一貫性をもたらし、特に
math/big
パッケージのような数値演算ライブラリにおいて、メソッドの役割とレシーバの意図をより明確にすることで、コードの可読性と保守性を大幅に向上させます。開発者は、レシーバ名を見るだけで、そのメソッドが結果を生成する演算なのか、それともレシーバ自身を操作するのかを直感的に理解できるようになります。
関連リンク
- Gerrit Change-ID:
https://golang.org/cl/5624043
(GoプロジェクトのコードレビューシステムであるGerritにおけるこの変更のID)
参考にした情報源リンク
- Go言語の公式ドキュメンテーション:
math/big
パッケージ - Go言語のコードレビューコメントやスタイルガイドに関する一般的な情報源 (Goコミュニティの慣習について)