[インデックス 13756] ファイルの概要
このコミットは、Go言語の実験的なexp/locale/collate/buildパッケージ内のコード整理とリファクタリングを目的としています。具体的には、複数の関数とentry構造体の定義が、より適切なファイルへと移動されました。これにより、コードのモジュール性が向上し、各ファイルの責務が明確化されています。
コミット
commit f0a31b5fc2e64ad1c597a5efc72749ab77058b87
Author: Marcel van Lohuizen <mpvl@golang.org>
Date: Thu Sep 6 13:16:02 2012 +0900
exp/locale/collate/build: moved some of the code to the appropriate file, as
promised in CL 13985.
R=r
CC=golang-dev
https://golang.org/cl/6503071
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/f0a31b5fc2e64ad1c597a5efc72749ab77058b87
元コミット内容
このコミットは、exp/locale/collate/buildパッケージ内のコードを整理し、一部のコードを適切なファイルに移動したものです。これは、以前の変更リスト(CL 13985)で約束されていたコード整理の一環として行われました。
具体的には、以下の変更が含まれます。
src/pkg/exp/locale/collate/build/builder.goから、entry構造体とその関連メソッド、baseColElem、colElem、equalCE、equalCEArrays、convertLargeWeights関数が削除されました。src/pkg/exp/locale/collate/build/colelem.goに、convertLargeWeights、equalCE、equalCEArrays関数が追加されました。src/pkg/exp/locale/collate/build/order.goに、entry構造体とその関連メソッド、そして新たにencodeBaseとencodeメソッドが追加されました。
これにより、builder.goはコレーションテーブルの構築ロジックに特化し、colelem.goはコレーション要素の操作、order.goはコレーション順序に関連するデータ構造とエンコーディングロジックをそれぞれ担当するようになりました。
変更の背景
この変更の背景には、Go言語のexp/locale/collateパッケージの設計と進化があります。このパッケージは、Unicode Collation Algorithm (UCA) に基づくテキストのソート(コレーション)機能を提供することを目的としています。初期の段階では、機能の実装が優先され、コードが単一のファイルに集中しがちでした。しかし、プロジェクトが成熟し、機能が追加されるにつれて、コードベースの保守性と理解しやすさを向上させるために、適切なモジュール分割と責務の明確化が必要となります。
コミットメッセージにある「promised in CL 13985」という記述から、このコード移動が以前のコミット(CL 13985)で計画されていた、あるいはそのコミットによって必要とされた整理作業であることがわかります。これは、開発プロセスにおいて、段階的な改善とリファクタリングが意識的に行われていることを示しています。特に、builder.goが肥大化し、複数の異なる責務を持つコードが混在していたため、それぞれの機能に応じたファイルへの分割が決定されたと考えられます。これにより、将来的な機能追加やバグ修正が容易になり、コードの可読性も向上します。
前提知識の解説
このコミットを理解するためには、以下の概念が前提知識として必要です。
1. Unicode Collation Algorithm (UCA)
UCAは、Unicode文字列を言語的に正しい順序でソートするための国際標準アルゴリズムです。単なるコードポイント順のソートではなく、言語固有のルール(例: ドイツ語のウムラウト、スペイン語のchやllの扱い、アクセント記号の無視など)を考慮に入れます。UCAは、プライマリ、セカンダリ、ターシャリ、クォータナリの4つのレベルの重み付け(Collation Element, CE)を使用して、文字列の比較を行います。
- プライマリ (Primary): 基本的な文字の順序を決定します。例えば、'a'と'b'の違い。
- セカンダリ (Secondary): アクセントやダイアクリティカルマークの違いを扱います。例えば、'a'と'á'の違い。
- ターシャリ (Tertiary): 大文字と小文字、および句読点の違いを扱います。例えば、'a'と'A'の違い。
- クォータナリ (Quaternary): 特定の言語(例: 日本語)や特殊なケース(例: 圧縮、拡張)で必要となる追加の比較ルールを扱います。
2. Collation Element (CE)
コレーション要素(CE)は、UCAにおいて各文字または文字シーケンスに割り当てられる数値の重み付けです。各CEは通常、プライマリ、セカンダリ、ターシャリの3つの部分から構成されます。文字列の比較は、これらのCEのシーケンスを比較することで行われます。例えば、文字'A'と'a'はプライマリ重みが同じで、ターシャリ重みが異なります。
3. DUCET (Default Unicode Collation Element Table)
DUCETは、Unicodeコンソーシアムによって提供される、UCAのデフォルトのコレーション要素テーブルです。これは、各Unicodeコードポイントまたはコードポイントシーケンスに対して、対応するコレーション要素を定義しています。多くの言語固有のコレーションルールは、このDUCETを基にして「テーラリング(tailoring)」と呼ばれるカスタマイズを行うことで実現されます。
4. テーラリング (Tailoring)
テーラリングとは、特定の言語やロケールに合わせて、DUCETのデフォルトのコレーションルールをカスタマイズすることです。例えば、ドイツ語ではäがaとeの間にソートされるようにルールを変更したり、特定の文字の重みを変更したりします。entry構造体内のprev, next, levelフィールドは、このテーラリングの情報を保持するために使用されます。
5. 拡張 (Expansion) と 圧縮 (Contraction)
- 拡張 (Expansion): 1つの文字が複数のコレーション要素にマッピングされるケースです。例えば、ドイツ語の
ßがssとして扱われる場合など。 - 圧縮 (Contraction): 複数の文字シーケンスが1つのコレーション要素にマッピングされるケースです。例えば、スペイン語の
chが1つの文字として扱われる場合など。
これらの概念は、exp/locale/collate/buildパッケージがコレーションテーブルを構築する際の基本的な要素となります。
技術的詳細
このコミットの技術的詳細は、Go言語のexp/locale/collate/buildパッケージが、Unicode Collation Algorithm (UCA) に基づくコレーションテーブルをどのように構築するか、その内部構造とロジックの整理に焦点を当てています。
entry構造体の移動と役割
以前はbuilder.goに定義されていたentry構造体は、order.goに移動されました。このentry構造体は、コレーション要素テーブル内の単一のエントリを追跡するために使用されます。そのフィールドは、文字(runes)、対応するコレーション要素(elems)、文字列表現(str)だけでなく、テーラリング(prev, next, level)、分解(decompose)、除外(exclude)、論理アンカー(logical)、拡張インデックス(expansionIndex)、圧縮ハンドル(contractionHandle)、圧縮インデックス(contractionIndex)といった、コレーションの複雑なルールを表現するためのメタデータを含んでいます。
order.goへの移動は、entryがコレーションの「順序」や「比較」に関連する基本的なデータ単位であるという責務を明確にするものです。
コレーション要素のエンコーディングロジックの分離
builder.goから削除されたbaseColElemとcolElem関数は、entry構造体のメソッドとしてencodeBase()とencode()に再構築され、order.goに移動されました。
encodeBase():entryの基本的なコレーション要素をエンコードします。拡張(expansion)の場合には拡張テーブルへのインデックスを、それ以外の場合には単一のコレーション要素を生成します。encode():entryの完全なコレーション要素をエンコードします。これは、分解(decomposition)、圧縮開始文字(contraction starter)、または通常の文字のいずれであるかに応じて、異なるロジックを適用します。特に、log.Fatalが使用されている箇所は、予期せぬ状態(例: スキップされるべきエントリのエンコード、圧縮が圧縮トライで処理されるべきなのにここで処理しようとするなど)を検出するためのガードとして機能します。
この変更により、コレーション要素のエンコーディングロジックがentry構造体自体にカプセル化され、builder.goはエンコーディングの詳細を知る必要がなくなり、より高レベルの構築ロジックに集中できるようになりました。
大規模な重み付けの変換ロジックの移動
convertLargeWeights関数は、builder.goからcolelem.goに移動されました。この関数は、DUCETで定義されているような、プライマリ重みが大きいコレーション要素(特にCJK文字や不正なルーン文字に関連するもの)を、Goの内部表現に変換する役割を担います。
DUCETでは、CJK文字などの一部の文字は、複数のコレーション要素(ダブルプライマリ)で表現されることがあります。この関数は、これらの特殊な重み付けを単一のコレーション要素に統合し、Goのコレーションシステムで効率的に扱えるようにします。colelem.goへの移動は、この関数がコレーション要素自体の操作と変換に密接に関連しているため、非常に適切です。
コレーション要素の比較ロジックの移動
equalCEとequalCEArrays関数も、builder.goからcolelem.goに移動されました。これらの関数は、コレーション要素([]int)やコレーション要素の配列([][]int)が等しいかどうかを比較するために使用されます。コレーション要素の比較は、コレーションシステムの中核的な操作であり、colelem.goがコレーション要素の定義と操作を扱うファイルであるため、この移動は自然なものです。
全体的な構造の改善
これらのコード移動により、exp/locale/collate/buildパッケージの構造は以下のように改善されました。
builder.go: コレーションテーブルの全体的な構築プロセスと、テーラリングの適用、最適化(simplify)などの高レベルなロジックに特化。colelem.go: コレーション要素の定義、変換、比較といった、コレーション要素自体の低レベルな操作に特化。order.go: コレーション順序の基本的なデータ構造(entry)と、そのエンコーディング、および順序付けに関連するユーティリティ関数に特化。
この明確な責務の分離は、コードの保守性、拡張性、および理解しやすさを大幅に向上させます。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下の3つのファイルにまたがっています。
-
src/pkg/exp/locale/collate/build/builder.go:entry構造体とその関連メソッド(String,skip,expansion,contraction,contractionStarter)の定義が削除されました。baseColElem関数とcolElem関数が削除されました。equalCE関数とequalCEArrays関数が削除されました。convertLargeWeights関数が削除されました。processContractions関数内で、b.baseColElem(e)の呼び出しがe.encodeBase()に置き換えられました。buildTrie関数内で、b.colElem(e)の呼び出しがe.encode()に置き換えられました。
-
src/pkg/exp/locale/collate/build/colelem.go:convertLargeWeights関数が追加されました。equalCE関数が追加されました。equalCEArrays関数が追加されました。
-
src/pkg/exp/locale/collate/build/order.go:entry構造体とその関連メソッド(String,skip,expansion,contraction,contractionStarter)の定義が追加されました。encodeBaseメソッドがentry構造体に追加されました。encodeメソッドがentry構造体に追加されました。
これらの変更は、コードの機能的な移動と、それに伴う呼び出し元の更新を反映しています。
コアとなるコードの解説
builder.go からの削除と呼び出し元の変更
builder.goから削除されたコードは、他のファイルに移動されたため、ここではその移動先での役割を説明します。重要なのは、builder.goがこれらの低レベルな詳細から解放され、より高レベルなコレーションテーブル構築のオーケストレーションに集中できるようになった点です。
processContractionsとbuildTrie関数における呼び出し元の変更は、このリファクタリングの直接的な結果です。
t.contractElem = append(t.contractElem, e.encodeBase())(旧:b.baseColElem(e))t.insert(e.runes[0], ce)(旧:b.colElem(e))
これは、コレーション要素のエンコーディングロジックがentry型にカプセル化されたことを示しています。builderはもはやエンコーディング方法を知る必要がなく、entryオブジェクトにその処理を委譲する形になりました。
colelem.go に追加された関数
func convertLargeWeights(elems [][]int) (res [][]int, err error)
この関数は、Unicode Collation Algorithm (UCA) の Default Unicode Collation Element Table (DUCET) において、特にCJK文字や不正なルーン文字に割り当てられる「大規模なプライマリ重み」を持つコレーション要素を、Goの内部表現に変換するために使用されます。
- 背景: DUCETでは、一部の文字(特にCJK統合漢字など)は、複数のコレーション要素(いわゆる「ダブルプライマリ」)で表現されることがあります。これは、限られたビット幅で膨大な数の文字を区別するための工夫です。また、UCAには「不正なプライマリ」という概念もあり、これらも特殊な重み付けを持ちます。
- 処理:
cjkPrimaryStart,rarePrimaryStart,otherPrimaryStart,illegalPrimaryなどの定数を用いて、プライマリ重みの範囲を定義します。- 各コレーション要素(
ce)のプライマリ重み(p)をチェックします。 p >= illegalPrimaryの場合、不正なプライマリとしてオフセットを適用します。- それ以外の場合(ダブルプライマリの可能性)、次のコレーション要素も参照し、2つの要素から新しい単一のプライマリ重み
npを計算します。この計算には、ビットシフトやマスク操作が含まれ、DUCETの仕様に従って重みを統合します。 - 統合後、元の2つの要素のうち2番目の要素を削除し、配列を詰めます。
- 目的: この変換により、Goのコレーションシステムがこれらの特殊な重み付けを効率的かつ正確に処理できるようになります。
func equalCE(a, b []int) bool
2つのコレーション要素([]int型、通常はプライマリ、セカンダリ、ターシャリの3つの要素を持つ)が等しいかどうかを比較します。
- まず、スライスの長さが異なる場合は
falseを返します。 - 次に、最初の3つの要素(プライマリ、セカンダリ、ターシャリ)を比較し、すべてが等しければ
trueを返します。
func equalCEArrays(a, b [][]int) bool
2つのコレーション要素の配列([][]int型)が等しいかどうかを比較します。
- まず、外側のスライスの長さが異なる場合は
falseを返します。 - 次に、各インデックスのコレーション要素ペアに対して
equalCEを呼び出し、すべてのペアが等しければtrueを返します。
これらの比較関数は、コレーション要素の同一性を確認するために、コレーションテーブル構築プロセス全体で利用されます。
order.go に追加されたentry構造体とメソッド
type entry struct { ... }
entry構造体は、コレーション要素テーブル内の単一のエントリを表します。そのフィールドは、前述の「前提知識の解説」で説明したように、文字、コレーション要素、テーラリング情報、拡張・圧縮関連のメタデータなど、コレーションの複雑な側面を表現するために使用されます。
func (e *entry) String() string
entry構造体のデバッグ表示用のメソッドです。fmt.Sprintfを使用して、ルーン、コレーション要素、圧縮ハンドル、圧縮インデックス、拡張インデックスなどの情報を整形して出力します。
func (e *entry) skip() bool
このエントリがコレーションテーブルの構築時にスキップされるべきかどうかを判断します。現在の実装では、e.contraction()がtrueの場合(つまり、このエントリが複数のルーンからなる圧縮である場合)にスキップされます。これは、圧縮は特別な方法で処理されるため、通常のコレーション要素として直接テーブルに追加されないことを意味します。
func (e *entry) expansion() bool
このエントリが拡張(expansion)であるかどうかを判断します。!e.decompose && len(e.elems) > 1の場合にtrueを返します。つまり、NFKD分解を使用せず、かつ複数のコレーション要素にマッピングされる場合に拡張と見なされます。
func (e *entry) contraction() bool
このエントリが圧縮(contraction)であるかどうかを判断します。len(e.runes) > 1の場合にtrueを返します。つまり、複数のルーンから構成される場合に圧縮と見なされます。
func (e *entry) contractionStarter() bool
このエントリが圧縮の開始文字であるかどうかを判断します。e.contractionHandle.n != 0の場合にtrueを返します。contractionHandleは、このエントリがどの圧縮シーケンスの開始点であるかを示す識別子です。
func (e *entry) encodeBase() (ce uint32, err error)
entryの基本的なコレーション要素をuint32形式でエンコードします。
e.expansion()がtrueの場合、makeExpandIndex関数を使用して拡張テーブルへのインデックスをエンコードします。e.decomposeがtrueの場合、log.Fatalを呼び出します。これは、分解は別の場所で処理されるべきであり、ここでエンコードされるべきではないという設計上の制約を示しています。- それ以外の場合、
makeCE関数を使用して最初のコレーション要素(e.elems[0])をエンコードします。
このメソッドは、主に圧縮シーケンス内の個々の要素のエンコードに使用されます。
func (e *entry) encode() (ce uint32, err error)
entryの完全なコレーション要素をuint32形式でエンコードします。これは、encodeBaseよりも高レベルなエンコーディングロジックを提供します。
e.skip()がtrueの場合、log.Fatalを呼び出します。これは、スキップされるべきエントリがエンコードされようとしている異常な状態を示します。e.decomposeがtrueの場合、makeDecompose関数を使用して分解されたコレーション要素をエンコードします。e.contractionStarter()がtrueの場合、makeContractIndex関数を使用して圧縮インデックスをエンコードします。- それ以外の場合(通常の文字または単一ルーンの拡張)、
e.contraction()がtrueであればlog.Fatalを呼び出します(圧縮は圧縮トライで処理されるべき)。そうでなければ、e.encodeBase()を呼び出して基本的なコレーション要素をエンコードします。
このメソッドは、コレーションテーブルに直接挿入されるコレーション要素のエンコードに使用されます。
これらのメソッドの移動と再構築により、entry構造体は自身の状態に基づいてコレーション要素を適切にエンコードする責務を持つようになり、コードの凝集度が高まりました。
関連リンク
- Unicode Collation Algorithm (UCA): https://unicode.org/reports/tr10/
- Default Unicode Collation Element Table (DUCET): http://www.unicode.org/Public/UCA/latest/allkeys.txt (最新版)
- Go言語の
x/text/collateパッケージ (実験的なexp/locale/collateの後継): https://pkg.go.dev/golang.org/x/text/collate
参考にした情報源リンク
- Go言語のコミット履歴 (GitHub): https://github.com/golang/go/commits/master
- Go言語のCL (Change List) 検索: https://go.dev/cl/
- Unicode Consortium: https://unicode.org/
- Go言語のドキュメント: https://go.dev/doc/