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

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

このコミットは、Go言語の標準ライブラリである strconv パッケージのAPIを刷新し、より一貫性のある命名規則と機能統合を導入するものです。文字列と数値、真偽値、引用符付き文字列間の変換を行う関数群が広範囲にわたって変更されています。

コミット

commit efbeaedb64e426f6874468ea4095d509622514df
Author: Russ Cox <rsc@golang.org>
Date:   Mon Dec 5 15:48:21 2011 -0500

    strconv: new API
    
    R=golang-dev, bradfitz, gri, r, agl
    CC=golang-dev
    https://golang.org/cl/5434095

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

https://github.com/golang/go/commit/efbeaedb64e426f6874468ea4095d509622514df

元コミット内容

strconv: new API

変更の背景

このコミットの主な背景は、Go言語の strconv パッケージにおけるAPIの一貫性と使いやすさの向上です。以前のAPIは、文字列から数値への変換(Ato*)、数値から文字列への変換(*toa)、特定のビットサイズへの変換(*N)など、機能ごとに異なる命名規則が混在していました。これにより、開発者がどの関数を使用すべきか迷う可能性がありました。

この変更は、以下の目的を達成するために行われました。

  1. 命名規則の統一: Parse* (文字列から型へ)、Format* (型から文字列へ)、Append* (バイトスライスに文字列形式を追加) という明確な命名規則を導入し、APIの予測可能性を高める。
  2. 機能の統合: 浮動小数点数や整数のパースにおいて、ビットサイズを引数として渡すことで、複数の専用関数(例: Atof32, Atof64, AtofN)を単一の関数(ParseFloat)に統合し、APIの数を削減する。
  3. 効率性の向上: Append* 関数群を導入することで、既存のバイトスライスに変換結果を直接追加できるようになり、不要なメモリ割り当てを減らし、パフォーマンスを向上させる。これは、特にループ内で文字列変換を頻繁に行う場合に有効です。
  4. Go言語のイディオムへの準拠: Go言語の設計思想である「シンプルさ」と「明瞭さ」に沿ったAPIを提供することを目指しています。

これらの変更により、strconv パッケージはより直感的で、効率的で、Go言語の他の部分との整合性が高まることが期待されます。

前提知識の解説

このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念と strconv パッケージの役割について理解しておく必要があります。

  • strconv パッケージ: Go言語の標準ライブラリの一部で、プリミティブ型(真偽値、整数、浮動小数点数)と文字列の間で変換を行うための機能を提供します。例えば、文字列 "123" を整数 123 に変換したり、浮動小数点数 3.14 を文字列 "3.14" に変換したりする際に使用されます。
  • 型変換(Parsing/Formatting):
    • Parsing (パース): 文字列形式のデータを、対応するGoのデータ型(例: int, float64, bool)に変換するプロセスです。例えば、"123"int(123) に変換します。
    • Formatting (フォーマット): Goのデータ型を、その文字列表現に変換するプロセスです。例えば、int(123)"123" に変換します。
  • ビットサイズ(bitSize): 整数型や浮動小数点数型が占めるメモリ上のビット数を指します。Goでは、int8, int16, int32, int64float32, float64 など、異なるビットサイズの型が提供されています。strconv パッケージの変換関数では、このビットサイズを指定することで、変換後の数値が特定の型に収まるように制御できます。
  • append 関数: Go言語の組み込み関数で、スライスに要素を追加するために使用されます。このコミットで導入される Append* 関数は、変換結果の文字列を既存のバイトスライスに効率的に追加するために利用されます。
  • エラーハンドリング: Go言語では、関数がエラーを返す場合、通常は戻り値の最後の要素として error 型の値を返します。strconv パッケージの変換関数も、変換に失敗した場合に *NumError 型のエラーを返します。
  • IEEE 754 浮動小数点数標準: 浮動小数点数の表現と演算に関する国際標準です。strconv パッケージの浮動小数点数変換は、この標準に準拠しています。特に、丸め処理(unbiased rounding)は、最も近い偶数への丸め(round half to even)を意味し、統計的なバイアスを避けるために使用されます。

これらの知識があることで、コミットがなぜ特定の関数名を変更し、新しい引数を導入し、特定の関数を追加したのかを深く理解することができます。

技術的詳細

このコミットは、strconv パッケージのAPIを大幅に再設計しています。主要な変更点は以下の通りです。

  1. 命名規則の統一と機能の統合:

    • 真偽値変換:
      • Atob(str string) (value bool, err error)ParseBool(str string) (value bool, err error) に変更されました。
      • Btoa(b bool) stringFormatBool(b bool) string に変更されました。
      • 新たに AppendBool(dst []byte, b bool) []byte が追加され、バイトスライスへの効率的な追加が可能になりました。
    • 浮動小数点数変換:
      • Atof32(s string) (f float32, err error), Atof64(s string) (f float64, err error), AtofN(s string, n int) (f float64, err error) の3つの関数が、単一の ParseFloat(s string, bitSize int) (f float64, err error) に統合されました。bitSize 引数(32または64)によって、パースする浮動小数点数の精度を指定します。戻り値は常に float64 ですが、bitSize=32 の場合は float32 に変換しても値が変わらないことが保証されます。
      • Ftoa32(f float32, fmt byte, prec int) string, Ftoa64(f float64, fmt byte, prec int) string, FtoaN(f float64, fmt byte, prec int, n int) string の3つの関数が、単一の FormatFloat(f float64, fmt byte, prec int, bitSize int) string に統合されました。同様に bitSize 引数で元の浮動小数点数の精度を指定します。
      • 新たに AppendFloat(dst []byte, f float64, fmt byte, prec int, bitSize int) []byte が追加されました。
    • 整数変換:
      • Btoui64(s string, b int) (n uint64, err error)ParseUint(s string, base int, bitSize int) (n uint64, err error) に変更されました。bitSize 引数(0, 8, 16, 32, 64)で、結果が収まるべき符号なし整数型を指定します。bitSize=0int または uint のデフォルトサイズを意味します。
      • Btoi64(s string, base int) (i int64, err error)ParseInt(s string, base int, bitSize int) (i int64, err error) に変更されました。同様に bitSize 引数で符号付き整数型を指定します。
      • Atoui64, Atoui, Atoi64, Atoi といった特定の型や基数に特化した関数は削除または簡略化され、ParseUintParseInt のラッパーとして再定義されました。例えば、Atoi(s string) (i int, err error)ParseInt(s, 10, 0) のショートハンドとなりました。
      • Uitob64(u uint64, base uint) stringFormatUint(i uint64, base int) string に変更されました。
      • Itob64(i int64, base uint) stringFormatInt(i int64, base int) string に変更されました。
      • Itoa64, Uitoa64, Uitob, Itob, Uitoa といった関数は削除または簡略化され、FormatUintFormatInt のラッパーとして再定義されました。
      • 新たに AppendInt(dst []byte, i int64, base int) []byteAppendUint(dst []byte, i uint64, base int) []byte が追加されました。
    • 引用符付き文字列変換:
      • Quote, QuoteToASCII, QuoteRune, QuoteRuneToASCII に対応する AppendQuote, AppendQuoteToASCII, AppendQuoteRune, AppendQuoteRuneToASCII 関数が追加されました。これらは、変換結果を既存のバイトスライスに直接追加します。
  2. IntSize 定数の変更:

    • 以前は computeIntsize() 関数で実行時に計算されていた IntSizeint および uint のビット数)が、コンパイル時に決定される定数 const intSize = 32 << uint(^uint(0)>>63) に変更されました。これは、Goのコンパイラがターゲットアーキテクチャのポインタサイズに基づいて int のサイズを決定できるようになったためです。^uint(0) はすべてのビットが1の uint 値を生成し、>>63 はその値が64ビットシステムでは0、32ビットシステムでは1になることを利用して、シフト量を調整しています。

これらの変更により、strconv パッケージのAPIはより統一され、柔軟性が増し、パフォーマンスが向上しました。特に、bitSize 引数の導入により、異なる数値型への変換を単一の関数で扱えるようになった点は大きな改善です。また、Append* 関数群は、文字列操作におけるメモリ効率を高める上で重要な役割を果たします。

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

このコミットでは、src/pkg/strconv ディレクトリ内の複数のファイルが変更されています。主要な変更箇所は以下の通りです。

  • src/pkg/strconv/atob.go:
    • Atob 関数が ParseBool にリネーム。
    • Btoa 関数が FormatBool にリネーム。
    • AppendBool 関数が新規追加。
  • src/pkg/strconv/atof.go:
    • Atof32, Atof64, AtofN 関数が削除され、ParseFloat 関数が新規追加。ParseFloatbitSize 引数を受け取る。
    • 内部関数 atof32atof64 が追加され、ParseFloat から呼び出される。
  • src/pkg/strconv/atoi.go:
    • Btoui64 関数が ParseUint にリネームされ、bitSize 引数が追加。
    • Btoi64 関数が ParseInt にリネームされ、bitSize 引数が追加。
    • Atoui64, Atoui, Atoi64, Atoi 関数が削除または簡略化され、ParseUintParseInt のラッパーとして再定義。
    • IntSize が実行時計算からコンパイル時定数に変更。
  • src/pkg/strconv/ftoa.go:
    • Ftoa32, Ftoa64, FtoaN 関数が削除され、FormatFloat 関数が新規追加。FormatFloatbitSize 引数を受け取る。
    • AppendFloat 関数が新規追加。
  • src/pkg/strconv/itoa.go:
    • Uitob64 関数が FormatUint にリネームされ、base 引数の型が uint から int に変更。
    • Itob64 関数が FormatInt にリネームされ、base 引数の型が uint から int に変更。
    • Itoa64, Uitoa64, Uitob, Itob, Uitoa 関数が削除または簡略化され、FormatUintFormatInt のラッパーとして再定義。
    • AppendInt および AppendUint 関数が新規追加。
  • src/pkg/strconv/quote.go:
    • AppendQuote, AppendQuoteToASCII, AppendQuoteRune, AppendQuoteRuneToASCII 関数が新規追加。
  • 対応する _test.go ファイル群:
    • 上記API変更に伴い、各テストファイル内の関数呼び出しやテストケースが新しいAPIに合わせて更新されています。

これらの変更は、strconv パッケージ全体のAPI設計思想を根本的に変更するものであり、Go言語の進化における重要なステップの一つと言えます。

コアとなるコードの解説

ここでは、変更された主要な関数とその意図について、具体的なコードスニペットを交えながら解説します。

ParseBool (旧 Atob)

// 旧: func Atob(str string) (value bool, err error) { ... }
// 新: func ParseBool(str string) (value bool, err error) {
//     switch str {
//     case "1", "t", "T", "true", "TRUE", "True":
//         return true, nil
//     case "0", "f", "F", "false", "FALSE", "False":
//         return false, nil
//     }
//     return false, &NumError{str, ErrSyntax}
// }

Atob から ParseBool へのリネームは、文字列を真偽値に「パースする」という関数の役割をより明確に示しています。Go言語の標準ライブラリでは、文字列から値を変換する関数には Parse プレフィックスを使用する慣習があります。

ParseFloat (旧 Atof32, Atof64, AtofN)

// 旧: func Atof32(s string) (f float32, err error) { ... }
// 旧: func Atof64(s string) (f float64, err error) { ... }
// 旧: func AtofN(s string, n int) (f float64, err error) { ... }
// 新: func ParseFloat(s string, bitSize int) (f float64, err error) {
//     if bitSize == 32 {
//         f1, err1 := atof32(s) // 内部関数
//         return float64(f1), err1
//     }
//     f1, err1 := atof64(s) // 内部関数
//     return f1, err1
// }

これは最も重要な変更点の一つです。異なるビットサイズの浮動小数点数パースを ParseFloat という単一の関数に統合しました。bitSize 引数によって、32ビット(float32)または64ビット(float64)の精度でパースするかを指定します。これにより、APIの複雑さが大幅に軽減され、開発者はより汎用的な関数を使用できるようになりました。戻り値が常に float64 であるのは、Goの型システムにおける浮動小数点数の扱いを簡素化するためです。

ParseUint (旧 Btoui64) と ParseInt (旧 Btoi64)

// 旧: func Btoui64(s string, b int) (n uint64, err error) { ... }
// 新: func ParseUint(s string, b int, bitSize int) (n uint64, err error) { ... }

// 旧: func Btoi64(s string, base int) (i int64, err error) { ... }
// 新: func ParseInt(s string, base int, bitSize int) (i int64, err error) { ... }

整数パース関数も ParseFloat と同様に bitSize 引数を導入し、特定のビットサイズ(8, 16, 32, 64)の整数をパースできるようになりました。これにより、Atoui, Atoi などの特定の型に特化した関数が不要になり、APIが整理されました。base 引数は、数値の基数(2進数から36進数まで)を指定します。

FormatFloat (旧 Ftoa32, Ftoa64, FtoaN)

// 旧: func Ftoa32(f float32, fmt byte, prec int) string { ... }
// 旧: func Ftoa64(f float64, fmt byte, prec int) string { ... }
// 旧: func FtoaN(f float64, fmt byte, prec int, n int) string { ... }
// 新: func FormatFloat(f float64, fmt byte, prec int, n int) string {
//     if n == 32 {
//         return genericFtoa(uint64(math.Float32bits(float32(f))), fmt, prec, &float32info)
//     }
//     return genericFtoa(math.Float64bits(f), fmt, prec, &float64info)
// }

浮動小数点数のフォーマットも FormatFloat に統合され、bitSize 引数で元の浮動小数点数の精度を指定できるようになりました。これにより、正確な丸め処理と桁数の決定が可能になります。

Append* 関数群

// 例: AppendBool
// func AppendBool(dst []byte, b bool) []byte {
//     if b {
//         return append(dst, "true"...)
//     }
//     return append(dst, "false"...)
// }

// 例: AppendInt
// func AppendInt(dst []byte, i int64, base int) []byte {
//     return append(dst, FormatInt(i, base)...)
// }

// 例: AppendQuote
// func AppendQuote(dst []byte, s string) []byte {
//     return append(dst, Quote(s)...)
// }

Append* 関数群は、既存のバイトスライス dst に変換結果の文字列を追加し、拡張されたバイトスライスを返す新しいパターンです。これは、特にパフォーマンスが重要なアプリケーションにおいて、文字列変換のたびに新しい文字列を生成するオーバーヘッドを削減するために導入されました。これにより、メモリ割り当てが減り、ガベージコレクションの負荷が軽減されます。

これらの変更は、strconv パッケージのAPIをより現代的で、Go言語のイディオムに沿ったものにし、開発者にとってより使いやすく、効率的なものにすることを目的としています。

関連リンク

参考にした情報源リンク