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

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

このコミットは、Go言語の実験的なロケールパッケージ exp/locale/collate における変更に関するものです。このパッケージは、Unicode Collation Algorithm (UCA) に基づいて文字列をロケール(地域や言語)固有のルールに従ってソート(照合)するための機能を提供します。具体的には、AlternateHandling という照合オプションのデフォルト値を変更し、国際的に広く利用されているICU (International Components for Unicode) ライブラリのデフォルト動作に合わせることを目的としています。

コミット

exp/locale/collate パッケージにおいて、AlternateHandling のデフォルト値を AltShifted から AltNonIgnorable に変更しました。これはICUのデフォルト設定と同じです。

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

https://github.com/golang/go/commit/2845e5881f80a717b33b3ab515e1306a7c0001dd

元コミット内容

    exp/locale/collate: changed default AlternateHandling to non-ignorable, the same
    default as ICU.
    
    R=r
    CC=golang-dev
    https://golang.org/cl/6445080

変更の背景

文字列の照合(ソート)は、言語や地域によってそのルールが大きく異なります。例えば、ドイツ語では 'ä' は 'a' と 'e' の間に来ることもあれば、'a' の後に来ることもあります。また、句読点や記号、スペースといった「変数文字 (variable characters)」の扱いも、ソートの目的によって変える必要があります。

このコミットが行われた当時、Go言語の exp/locale/collate パッケージは、AlternateHandling のデフォルト値として AltShifted を採用していました。しかし、Unicode Collation Algorithm (UCA) のリファレンス実装として広く使われているICUライブラリでは、デフォルトで AltNonIgnorable が採用されています。

この不一致は、Goの照合結果がICUのそれと異なる可能性を生み出し、国際化されたアプリケーション開発において予期せぬソート順序の問題を引き起こす可能性がありました。特に、多くの開発者がICUの動作を標準的なものとして認識しているため、Goのパッケージもそれに合わせることで、より直感的で互換性の高い動作を提供できるようになります。

したがって、この変更の背景には、Goの国際化関連パッケージの標準化と、既存の国際的な慣習(ICUのデフォルト)への準拠という目的があります。

前提知識の解説

1. コレーション (Collation)

コレーションとは、文字列を特定の言語やロケールのルールに従って比較し、ソート順序を決定するプロセスです。単に文字コードの順序で比較するのではなく、アクセント記号、大文字・小文字、句読点、複合文字(例: ドイツ語のßとss)などを考慮に入れます。

2. Unicode Collation Algorithm (UCA)

UCAは、Unicode Consortiumによって定義された、Unicode文字列の比較に関する標準アルゴリズムです。これにより、異なるプラットフォームやアプリケーション間でも一貫したソート順序が保証されます。UCAは、文字列を複数の「レベル」で比較します。

  • レベル1 (Primary Level): 基本的な文字の形状(例: 'a' と 'A' は同じとみなされる)。
  • レベル2 (Secondary Level): アクセント記号やダイアクリティカルマーク(例: 'a' と 'á' は異なる)。
  • レベル3 (Tertiary Level): 大文字・小文字の違い(例: 'a' と 'A' は異なる)。
  • レベル4 (Quaternary Level): AlternateHandlingShifted の場合にのみ使用され、変数文字のソート順を決定します。

3. ICU (International Components for Unicode)

ICUは、Unicodeおよび国際化対応のためのオープンソースライブラリ群です。C/C++およびJavaで提供されており、コレーション、日付・時刻のフォーマット、数値のフォーマット、テキストの境界検出など、多岐にわたる国際化機能を提供します。多くのオペレーティングシステムやアプリケーションで利用されており、UCAの事実上のリファレンス実装として機能しています。

4. Alternate Handling (代替処理)

AlternateHandling は、UCAにおける重要な設定の一つで、句読点、記号、スペースなどの「変数文字 (variable characters)」をソート順にどのように組み込むかを制御します。主な設定値は以下の通りです。

  • AltNonIgnorable (非無視可能):

    • 変数文字は、プライマリ、セカンダリ、ターシャリのすべてのレベルでソート順に影響を与えます。
    • 例: "a-b" と "ab" を比較すると、ハイフンがソート順に影響します。
    • これは、文字通りすべての文字がソートに寄与する「ワードソート」や「シンボルソート」に適しています。ICUのデフォルト設定です。
  • AltShifted (シフト):

    • 変数文字は、プライマリ、セカンダリ、ターシャリのレベルでは無視されます。
    • しかし、これらの文字は第4のレベル(クォータナリレベル)で考慮され、ソート順に影響を与えます。
    • 例: "a-b" と "ab" を比較すると、まず「ab」として比較され、その後にハイフンの有無が考慮されます。
    • これは、句読点やスペースが単語のソート順に影響を与えない「辞書順ソート」に適しています。
  • AltBlanked:

    • 変数文字は、すべてのレベルで完全に無視されます。これは、変数文字を文字列から削除して比較するのと同等です。

このコミットは、Goの exp/locale/collate パッケージのデフォルトの AlternateHandlingAltShifted から AltNonIgnorable に変更することで、ICUのデフォルト動作に合わせ、より標準的なコレーション動作を提供することを目指しています。

技術的詳細

Go言語では、iota キーワードを使って連続する整数定数を定義できます。iotaconst ブロック内で0から始まり、各定数宣言ごとに1ずつ増加します。

変更前の AlternateHandling 定義は以下のようになっていました。

const (
	// AltShifted sets variables to be ignorable for levels one through three and
	// adds a fourth level based on the values of the ignored levels.
	AltShifted AlternateHandling = iota // AltShifted = 0
	// AltNonIgnorable turns off special handling of variables.
	AltNonIgnorable                    // AltNonIgnorable = 1
	// AltBlanked sets variables and all subsequent primary ignorables to be
	// ignorable at all levels. This is identical to removing all variables
	// and subsequent primary ignorables from the input.
	AltBlanked                         // AltBlanked = 2

	// AltShiftTrimmed is a slight variant of AltShifted that is used to
	// emulate POSIX.
	AltShiftTrimmed                    // AltShiftTrimmed = 3
)

この定義では、AlternateHandling 型の変数が明示的に初期化されない場合、Goのゼロ値セマンティクスにより AltShifted (値が0) がデフォルトとして使用されていました。

このコミットでは、AlternateHandling 定数の順序が変更されました。

const (
	// AltNonIgnorable turns off special handling of variables.
	AltNonIgnorable AlternateHandling = iota // AltNonIgnorable = 0
	// AltBlanked sets variables and all subsequent primary ignorables to be
	// ignorable at all levels. This is identical to removing all variables
	// and subsequent primary ignorables from the input.
	AltBlanked                               // AltBlanked = 1

	// AltShifted sets variables to be ignorable for levels one through three and
	// adds a fourth level based on the values of the ignored levels.
	AltShifted                               // AltShifted = 2

	// AltShiftTrimmed is a slight variant of AltShifted that is used to
	// emulate POSIX.
	AltShiftTrimmed                          // AltShiftTrimmed = 3
)

この変更により、iota の割り当て順序が変わり、AltNonIgnorable が0の値を持つようになりました。結果として、AlternateHandling 型の変数が明示的に設定されない場合、そのデフォルト値は AltNonIgnorable となります。

このデフォルト値の変更に伴い、既存のテストコードも修正されています。以前は AltShifted がデフォルトであったため、テストケースによっては AlternateHandling を明示的に指定していなくても AltShifted の動作が期待されていました。しかし、デフォルトが AltNonIgnorable に変わったため、これらのテストケースが引き続き AltShifted の動作を検証するためには、opts 構造体内で alt: collate.AltShifted を明示的に設定する必要が生じました。

また、TestKey 関数内でも、特定のコレーターインスタンス c に対して c.Alternate = collate.AltShifted と明示的に設定することで、そのテストが意図する AltShifted の動作を保証しています。

この技術的な変更は、Goのコレーションパッケージが、国際的な標準であるICUのデフォルト動作に準拠し、より予測可能で互換性のある挙動を提供する上で重要な一歩となります。

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

src/pkg/exp/locale/collate/collate.go

AlternateHandling 型の定数定義の順序が変更されました。

--- a/src/pkg/exp/locale/collate/collate.go
+++ b/src/pkg/exp/locale/collate/collate.go
@@ -35,18 +35,18 @@ const (
 type AlternateHandling int
 
 const (
--	// AltShifted sets variables to be ignorable for levels one through three and
--	// adds a fourth level based on the values of the ignored levels.
--	AltShifted AlternateHandling = iota
--
- 	// AltNonIgnorable turns off special handling of variables.
--	AltNonIgnorable
-+	AltNonIgnorable AlternateHandling = iota
- 
- 	// AltBlanked sets variables and all subsequent primary ignorables to be
- 	// ignorable at all levels. This is identical to removing all variables
- 	// and subsequent primary ignorables from the input.
- 	AltBlanked
- 
-+	// AltShifted sets variables to be ignorable for levels one through three and
-+	// adds a fourth level based on the values of the ignored levels.
-+	AltShifted
-+
- 	// AltShiftTrimmed is a slight variant of AltShifted that is used to
- 	// emulate POSIX.
- 	AltShiftTrimmed

src/pkg/exp/locale/collate/collate_test.go

keyFromElemTests 配列内の複数のテストケースで、opts 構造体に alt: collate.AltShifted が追加されました。また、TestKey 関数内でコレーターの Alternate フィールドが明示的に設定されました。

--- a/src/pkg/exp/locale/collate/collate_test.go
+++ b/src/pkg/exp/locale/collate/collate_test.go
@@ -223,7 +223,7 @@ const sep = 0 // separator byte
 
 var keyFromElemTests = []keyFromElemTest{
 	{ // simple primary and secondary weights.
-		opts{},
+		opts{alt: collate.AltShifted},
 		ColElems{w(0x200), w(0x7FFF), w(0, 0x30), w(0x100)},
 		[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
 			sep, sep, 0, defS, 0, defS, 0, 0x30, 0, defS, // secondary
@@ -232,7 +232,7 @@ var keyFromElemTests = []keyFromElemTest{
 		},
 	},
 	{ // same as first, but with zero element that need to be removed
-		opts{},
+		opts{alt: collate.AltShifted},
 		ColElems{w(0x200), zero, w(0x7FFF), w(0, 0x30), zero, w(0x100)},
 		[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
 			sep, sep, 0, defS, 0, defS, 0, 0x30, 0, defS, // secondary
@@ -241,7 +241,7 @@ var keyFromElemTests = []keyFromElemTest{
 		},
 	},
 	{ // same as first, with large primary values
-		opts{},
+		opts{alt: collate.AltShifted},
 		ColElems{w(0x200), w(0x8000), w(0, 0x30), w(0x12345)},
 		[]byte{0x2, 0, 0x80, 0x80, 0x00, 0x81, 0x23, 0x45, // primary
 			sep, sep, 0, defS, 0, defS, 0, 0x30, 0, defS, // secondary
@@ -250,7 +250,7 @@ var keyFromElemTests = []keyFromElemTest{
 		},
 	},
 	{ // same as first, but with the secondary level backwards
-		opts{backwards: true},
+		opts{alt: collate.AltShifted, backwards: true},
 		ColElems{w(0x200), w(0x7FFF), w(0, 0x30), w(0x100)},
 		[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
 			sep, sep, 0, defS, 0, 0x30, 0, defS, 0, defS, // secondary
@@ -259,7 +259,7 @@ var keyFromElemTests = []keyFromElemTest{
 		},
 	},
 	{ // same as first, ignoring quaternary level
-		opts{lev: 3},
+		opts{alt: collate.AltShifted, lev: 3},
 		ColElems{w(0x200), zero, w(0x7FFF), w(0, 0x30), zero, w(0x100)},
 		[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
 			sep, sep, 0, defS, 0, defS, 0, 0x30, 0, defS, // secondary
@@ -267,14 +267,14 @@ var keyFromElemTests = []keyFromElemTest{
 		},
 	},
 	{ // same as first, ignoring tertiary level
-		opts{lev: 2},
+		opts{alt: collate.AltShifted, lev: 2},
 		ColElems{w(0x200), zero, w(0x7FFF), w(0, 0x30), zero, w(0x100)},
 		[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
 			sep, sep, 0, defS, 0, defS, 0, 0x30, 0, defS, // secondary
 		},
 	},
 	{ // same as first, ignoring secondary level
-		opts{lev: 1},
+		opts{alt: collate.AltShifted, lev: 1},
 		ColElems{w(0x200), zero, w(0x7FFF), w(0, 0x30), zero, w(0x100)},
 		[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00},
 	},
@@ -288,7 +288,7 @@ var keyFromElemTests = []keyFromElemTest{
 		},
 	},
 	{ // as first, primary with case level enabled
-		opts{lev: 1, caseLevel: true},
+		opts{alt: collate.AltShifted, lev: 1, caseLevel: true},
 		ColElems{w(0x200), w(0x7FFF), w(0, 0x30), w(0x100)},
 		[]byte{0x2, 0, 0x7F, 0xFF, 0x1, 0x00, // primary
 			sep, sep, // secondary
@@ -378,6 +378,7 @@ var keyTests = []keyTest{
 
 func TestKey(t *testing.T) {
 	c, _ := makeTable(appendNextTests[4].in)
+	c.Alternate = collate.AltShifted
 	buf := collate.Buffer{}
 	keys1 := [][]byte{}
 	keys2 := [][]byte{}

コアとなるコードの解説

collate.go の変更

collate.go で行われた変更は、AlternateHandling 型の定数 AltNonIgnorableAltShifted の定義順序を入れ替えることです。Goの iotaconst ブロック内で0から始まる連番を自動的に割り当てるため、この順序変更によって AltNonIgnorable0 の値を持つようになります。

Goでは、構造体のフィールドが明示的に初期化されない場合、その型のゼロ値が割り当てられます。AlternateHandlingint 型のエイリアスであるため、そのゼロ値は 0 です。したがって、この変更により、AlternateHandling が指定されていないコレーターインスタンスは、デフォルトで AltNonIgnorable の動作をするようになります。これは、ICUのデフォルト動作と一致します。

collate_test.go の変更

collate_test.go の変更は、上記のデフォルト値の変更に対応するためのものです。

  1. keyFromElemTests の修正: keyFromElemTests は、コレーションキー生成のテストケースを定義しています。以前は、これらのテストケースの一部は AlternateHandling を明示的に設定していませんでしたが、当時のデフォルトである AltShifted の動作を暗黙的に期待していました。 collate.go の変更によりデフォルトが AltNonIgnorable になったため、これらのテストケースが引き続き AltShifted の動作を検証するためには、opts 構造体内で alt: collate.AltShifted を明示的に指定する必要が生じました。これにより、テストの意図が明確になり、デフォルト値の変更後もテストが正しく機能することが保証されます。

  2. TestKey 関数の修正: TestKey 関数内でも、コレーターインスタンス c を作成した後、c.Alternate = collate.AltShifted という行が追加されています。これは、この特定のテストが AltShifted のコレーション動作に依存しているため、デフォルト値の変更に関わらず、テストが常に AltShifted モードで実行されるようにするための明示的な設定です。

これらのテストの変更は、コードの動作変更(デフォルト値の変更)が、既存の期待される動作(テストケースが検証する動作)に影響を与えないようにするための、適切な対応と言えます。

関連リンク

参考にした情報源リンク

  • Go言語の iota に関するドキュメント
  • Unicode Collation Algorithm (UCA) の仕様
  • ICUのコレーションに関するドキュメント
  • Go言語のテストの書き方に関する一般的な知識