[インデックス 18497] ファイルの概要
このコミットは、Go言語のmath/big
パッケージに、任意精度有理数型であるRat
(Rational Number)のための汎用エンコーディングインターフェース(encoding.TextMarshaler
およびencoding.TextUnmarshaler
)のサポートを追加するものです。これにより、big.Rat
型の値をテキスト形式で簡単にシリアライズおよびデシリアライズできるようになり、特にJSONやXMLなどのデータフォーマットを介したデータ転送が容易になります。
コミット
commit eea28f6701ac888d5613470a88d09b634efc1d75
Author: Michael T. Jones <mtj@google.com>
Date: Thu Feb 13 08:42:19 2014 -0800
math/big: add support for general encoding interfaces
TextMarshaller and TextUnmarshaller to ease transport of
unlimited precision rational numbers.
Fixes #7287.
Consists of encode and decode functions and two test
functions, one using JSON and one using XML. Each
verifies round trips for integers (rationals with
denominator == 1) and for fractional vaues.
LGTM=gri
R=gri, cookieo9, bradfitz, mtj
CC=golang-codereviews
https://golang.org/cl/61180043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/eea28f6701ac888d5613470a88d09b634efc1d75
元コミット内容
このコミットの元の内容は、math/big
パッケージのRat
型に、encoding.TextMarshaler
とencoding.TextUnmarshaler
インターフェースのサポートを追加することです。これにより、無制限精度の有理数をテキスト形式で簡単に転送できるようになります。具体的には、エンコード関数とデコード関数、そしてJSONとXMLを使用した2つのテスト関数が含まれており、それぞれ整数(分母が1の有理数)と分数でのラウンドトリップ(エンコードとデコードが正しく行われること)を検証しています。
変更の背景
この変更の背景には、Go言語のmath/big
パッケージが提供する任意精度数値型(Int
, Float
, Rat
など)を、一般的なデータシリアライゼーションフォーマット(JSON, XMLなど)で扱う際の利便性の向上が挙げられます。特にRat
型のような複雑な構造を持つ型を、標準的なエンコーディングメカニズムで扱えるようにすることで、開発者はカスタムのシリアライゼーションロジックを記述することなく、これらの数値をネットワーク経由で送信したり、ファイルに保存したりすることが可能になります。
コミットメッセージにFixes #7287
とありますが、Goの公式リポジトリでこのIssue番号が見つからないため、内部的なIssue番号であるか、あるいは別の文脈での参照である可能性があります。しかし、この変更がユーザーからの要望や、より広範なGoエコシステムでのデータ交換のニーズに応えるものであることは明らかです。
前提知識の解説
Go言語のencoding
パッケージとインターフェース
Go言語の標準ライブラリには、データエンコーディングとデコーディングのための強力なencoding
パッケージ群が含まれています。これらのパッケージは、特定のデータフォーマット(例: encoding/json
, encoding/xml
, encoding/gob
など)に特化していますが、共通のインターフェースを介して動作します。
-
encoding.TextMarshaler
インターフェース: このインターフェースは、任意の型をテキスト形式に変換するためのMarshalText() ([]byte, error)
メソッドを定義します。このメソッドを実装することで、その型はJSONの文字列フィールドやXMLのテキストコンテンツとして自動的にエンコードされるようになります。 -
encoding.TextUnmarshaler
インターフェース: このインターフェースは、テキスト形式のデータを任意の型に変換するためのUnmarshalText(text []byte) error
メソッドを定義します。このメソッドを実装することで、その型はJSONの文字列フィールドやXMLのテキストコンテンツから自動的にデコードされるようになります。
これらのインターフェースを実装することで、開発者はカスタムのシリアライゼーションロジックを記述することなく、Goの標準的なエンコーディング/デコーディングメカニズムを利用して、複雑なデータ型を扱うことができます。
math/big
パッケージとRat
型
math/big
パッケージは、Go言語で任意精度の算術演算を行うための型を提供します。これは、標準のGoの数値型(int
, float64
など)が持つ精度や範囲の制限を超える計算が必要な場合に非常に有用です。
big.Rat
型:big.Rat
は、任意精度の有理数を表す型です。有理数は、整数を分子と分母で表される分数(例: 1/2, 3/4, -5/3)です。big.Rat
は、分母が1の場合には整数も表現できます。この型は、金融計算、科学技術計算、暗号学など、高い精度が要求される分野で利用されます。
JSONとXML
-
JSON (JavaScript Object Notation): 軽量なデータ交換フォーマットであり、人間が読み書きしやすく、機械が解析しやすいという特徴があります。WebアプリケーションのAPIなどで広く利用されています。
-
XML (Extensible Markup Language): 構造化されたデータを表現するためのマークアップ言語です。JSONよりも冗長ですが、より厳密なスキーマ定義や複雑なデータ構造の表現が可能です。
これらのフォーマットは、異なるシステム間でデータを交換する際の標準的な手段であり、Goのencoding/json
やencoding/xml
パッケージによってサポートされています。
技術的詳細
このコミットの主要な技術的詳細は、math/big.Rat
型がencoding.TextMarshaler
とencoding.TextUnmarshaler
インターフェースを実装した点にあります。
MarshalText()
の実装
Rat
型のMarshalText()
メソッドは、Rat
の値をその文字列表現(例: "1/2", "3", "-5/3")に変換します。これは、Rat
型が既に持っているRatString()
メソッドを利用することで実現されています。RatString()
は、Rat
の値を標準的な分数形式の文字列として返します。MarshalText()
は、この文字列をバイトスライスに変換して返します。
これにより、encoding/json
やencoding/xml
などのパッケージがRat
型の値をエンコードする際に、自動的にその文字列表現を使用するようになります。例えば、JSONにエンコードされると、{"value": "1/2"}
のように文字列として表現されます。
UnmarshalText()
の実装
Rat
型のUnmarshalText()
メソッドは、テキスト形式のバイトスライスを受け取り、それをRat
型の値にパースします。この実装では、Rat
型が既に持っているSetString()
メソッドを利用しています。SetString()
は、文字列を受け取り、それをRat
の値として設定します。パースが成功した場合はnil
エラーを返し、失敗した場合はfmt.Errorf
を使用してエラーを返します。
これにより、JSONやXMLからデコードする際に、文字列として表現された有理数をRat
型に正しく変換できるようになります。例えば、JSONの{"value": "1/2"}
という文字列から、対応するbig.Rat
オブジェクトが生成されます。
テストの追加
コミットには、JSONとXMLの両方でRat
型のエンコード/デコードが正しく機能するかを検証するテスト関数が含まれています。これらのテストは、様々な整数値と分数値を対象に、エンコード後にデコードし、元の値とデコードされた値が一致するか(ラウンドトリップが成功するか)を確認します。これにより、実装の正確性と堅牢性が保証されます。
特に、ratNums
とratDenoms
というスライスで定義された、非常に大きな数値や小さな数値、正負の数値、そして分母が1(整数)の場合とそうでない場合の両方を網羅するテストデータが用意されており、広範なケースでの動作が検証されています。
コアとなるコードの変更箇所
src/pkg/math/big/rat.go
// MarshalText implements the encoding.TextMarshaler interface
func (r *Rat) MarshalText() (text []byte, err error) {
return []byte(r.RatString()), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface
func (r *Rat) UnmarshalText(text []byte) error {
if _, ok := r.SetString(string(text)); !ok {
return fmt.Errorf("math/big: cannot unmarshal %q into a *big.Rat", text)
}
return nil
}
src/pkg/math/big/rat_test.go
import (
"encoding/json"
"encoding/xml"
// ...
)
var ratNums = []string{
"-141592653589793238462643383279502884197169399375105820974944592307816406286",
"-1415926535897932384626433832795028841971",
"-141592653589793",
"-1",
"0",
"1",
"141592653589793",
"1415926535897932384626433832795028841971",
"141592653589793238462643383279502884197169399375105820974944592307816406286",
}
var ratDenoms = []string{
"1",
"718281828459045",
"7182818284590452353602874713526624977572",
"718281828459045235360287471352662497757247093699959574966967627724076630353",
}
func TestRatJSONEncoding(t *testing.T) {
for _, num := range ratNums {
for _, denom := range ratDenoms {
var tx Rat
tx.SetString(num + "/" + denom)
b, err := json.Marshal(&tx)
if err != nil {
t.Errorf("marshaling of %s failed: %s", &tx, err)
continue
}
var rx Rat
if err := json.Unmarshal(b, &rx); err != nil {
t.Errorf("unmarshaling of %s failed: %s", &tx, err)
continue
}
if rx.Cmp(&tx) != 0 {
t.Errorf("JSON encoding of %s failed: got %s want %s", &tx, &rx, &tx)
}
}
}
}
func TestRatXMLEncoding(t *testing.T) {
for _, num := range ratNums {
for _, denom := range ratDenoms {
var tx Rat
tx.SetString(num + "/" + denom)
b, err := xml.Marshal(&tx)
if err != nil {
t.Errorf("marshaling of %s failed: %s", &tx, err)
continue
}
var rx Rat
if err := xml.Unmarshal(b, &rx); err != nil {
t.Errorf("unmarshaling of %s failed: %s", &tx, err)
continue
}
if rx.Cmp(&tx) != 0 {
t.Errorf("XML encoding of %s failed: got %s want %s", &tx, &rx, &tx)
}
}
}
}
コアとなるコードの解説
rat.go
の変更
-
MarshalText()
メソッド: このメソッドは*Rat
レシーバを持ち、encoding.TextMarshaler
インターフェースを実装します。内部ではr.RatString()
を呼び出してRat
の文字列表現を取得し、それを[]byte
に変換して返します。エラーは発生しないため、nil
を返します。これにより、Rat
型がJSONやXMLにエンコードされる際に、その文字列表現が使用されるようになります。 -
UnmarshalText()
メソッド: このメソッドは*Rat
レシーバを持ち、encoding.TextUnmarshaler
インターフェースを実装します。引数として[]byte
形式のテキストデータを受け取ります。このテキストデータをstring
に変換し、r.SetString()
メソッドに渡してRat
の値を設定しようと試みます。SetString()
は、文字列が有効な有理数形式でない場合にfalse
を返すため、その場合はfmt.Errorf
を使ってエラーメッセージを生成し、返します。これにより、テキスト形式の有理数文字列からRat
型へのデコードが可能になります。
rat_test.go
の変更
-
encoding/json
とencoding/xml
のインポート: テストのために、これらのパッケージが新しくインポートされています。 -
ratNums
とratDenoms
: テストに使用される分子と分母の文字列スライスが定義されています。これらは、非常に大きな数値、小さな数値、正負の数値、そして整数(分母が"1")と分数を含む多様なケースをカバーしています。 -
TestRatJSONEncoding()
関数: このテスト関数は、ratNums
とratDenoms
の全ての組み合わせに対して以下の処理を行います。Rat
オブジェクトtx
を生成し、SetString()
で分子と分母の組み合わせを設定します。json.Marshal(&tx)
を呼び出してtx
をJSONバイトスライスにエンコードします。エンコードに失敗した場合はエラーを報告します。- 新しい
Rat
オブジェクトrx
を生成し、json.Unmarshal(b, &rx)
を呼び出してエンコードされたJSONバイトスライスをrx
にデコードします。デコードに失敗した場合はエラーを報告します。 rx.Cmp(&tx) != 0
で、元のtx
とデコードされたrx
が等しいか(比較結果が0でないか)を比較します。等しくない場合は、JSONエンコーディング/デコーディングのラウンドトリップが失敗したとしてエラーを報告します。
-
TestRatXMLEncoding()
関数: このテスト関数はTestRatJSONEncoding()
とほぼ同じロジックですが、encoding/xml
パッケージを使用してXMLエンコーディング/デコーディングのラウンドトリップを検証します。
これらのテストは、Rat
型がencoding.TextMarshaler
とencoding.TextUnmarshaler
インターフェースを正しく実装し、JSONとXMLの両方で期待通りに動作することを保証します。
関連リンク
- Go言語
encoding
パッケージのドキュメント: https://pkg.go.dev/encoding - Go言語
encoding/json
パッケージのドキュメント: https://pkg.go.dev/encoding/json - Go言語
encoding/xml
パッケージのドキュメント: https://pkg.go.dev/encoding/xml - Go言語
math/big
パッケージのドキュメント: https://pkg.go.dev/math/big
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード (特に
src/pkg/math/big/rat.go
とsrc/pkg/math/big/rat_test.go
) - JSON (JavaScript Object Notation) 公式サイト
- XML (Extensible Markup Language) W3C勧告
- Go言語の
encoding
インターフェースに関する一般的な情報源