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

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

このコミットは、Go言語の実験的なexp/locale/collateパッケージにおけるBuilder APIの変更に関するものです。特に、CLDR (Common Locale Data Repository) ファイルの処理をより便利にするための改善が施されています。

コミット

commit 89d40b911c0e1f7012e2f463919d8093a49797cc
Author: Marcel van Lohuizen <mpvl@golang.org>
Date:   Fri Aug 3 09:01:21 2012 +0200

    exp/locale/collate: changed API of Builder to be more convenient
    for dealing with CLDR files:
    - Add now taxes a list of indexes of colelems that are variables. Checking and
      handling is now done by the Builder.  VariableTop is now also properly generated
      using the Build method.
    - Introduced separate Builder, called Tailoring, for creating tailorings of root
      table.  This clearly separates the functionality for building a table based on
      weights (the allkeys* files) versus tables based on LDML XML files.
    - Tailorings are now added by two calls instead of one: SetAnchor and Insert.
      This more closely reflects the structure of LDML side and simplifies the
      implementation of both the client and library side.  It also preserves
      some information that is otherwise hard to recover for the Builder.
    - Allow the LDML XML element extend to be passed to Insert.  This simplifies
      both client and library implementation.
    
    R=r
    CC=golang-dev
    https://golang.org/cl/6454061

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

https://github.com/golang/go/commit/89d40b911c0e1f7012e2f463919d8093a49797cc

元コミット内容

上記の「コミット」セクションに記載されている内容が元コミット内容です。

変更の背景

このコミットの背景には、Go言語のexp/locale/collateパッケージが、Unicode Collation Algorithm (UCA) および Common Locale Data Repository (CLDR) のデータに基づいて、文字列の照合(ソート)ルールを構築する際の利便性と正確性を向上させるという目的があります。

従来のBuilder APIは、CLDRの複雑なデータ構造、特に「テーラリング(Tailoring)」と呼ばれるロケール固有の照合ルールのカスタマイズを効率的に扱うには不十分でした。CLDRは、各言語や地域に特有のソート順序を定義するためにLDML (Locale Data Markup Language) というXML形式を使用しており、その中には「可変要素(Variable Collation Elements)」や「拡張(Extend)」といった概念が含まれます。

このコミットは、以下の課題を解決するために行われました。

  1. 可変要素の適切な処理: UCAでは、句読点や記号などの一部の文字を「可変要素」として扱い、照合の強度(Primary, Secondary, Tertiaryなど)に応じてその重みを無視したり、異なる方法で処理したりします。VariableTopという概念は、可変要素のプライマリ重みの上限を定義します。以前のAPIでは、この可変要素のチェックとVariableTopの生成が十分に自動化されていませんでした。
  2. テーラリング構築の複雑性: ルートの照合テーブル(UCAのデフォルトルール)に加えて、特定のロケール(例: ドイツ語、スウェーデン語)に合わせたテーラリングを構築するプロセスが、APIレベルで明確に分離されておらず、実装が複雑でした。特に、LDML XMLファイルで定義されるテーラリングは、重みファイル(allkeys*)から構築される基本テーブルとは異なるアプローチを必要とします。
  3. LDML構造との乖離: LDMLのテーラリングは、特定のアンカー(基準点)に対して要素を挿入するという構造を持っています。以前のAPIでは、これを単一のAddTailoring呼び出しで処理しようとしており、LDMLのセマンティクスを直接反映していませんでした。これにより、クライアント側とライブラリ側の両方で実装が複雑になり、一部の情報が失われる可能性がありました。
  4. LDML extend要素のサポート: LDMLには、特定の文字の照合要素に別の文字の照合要素を「拡張」するextend要素が存在します。これをAPIで直接サポートすることで、テーラリングの定義がより直感的になります。

これらの課題に対処するため、Builder APIの再設計が行われ、Tailoringという新しい概念の導入、テーラリング追加のためのSetAnchorInsertの分離、そしてAddメソッドでの可変要素の明示的な指定が導入されました。これにより、CLDRデータからの照合テーブル構築がより堅牢で、かつ開発者にとって扱いやすくなることを目指しています。

前提知識の解説

このコミットの変更内容を理解するためには、以下の概念について理解しておく必要があります。

1. 文字コードとUnicode

  • 文字コード: コンピュータが文字を扱うための符号化方式。
  • Unicode: 世界中のあらゆる文字を統一的に扱うための文字コード標準。

2. 文字列の照合(Collation)

文字列の照合とは、文字列を特定の順序で並べ替える(ソートする)プロセスです。単純なバイナリ比較ではなく、言語や文化に特有のルールに基づいて行われます。例えば、英語では大文字と小文字を区別しないソート、ドイツ語ではウムラウト文字の扱い、スウェーデン語では特定の文字がアルファベットの最後にくる、といったルールがあります。

3. Unicode Collation Algorithm (UCA)

UCAは、Unicode Consortiumによって定義された、多言語対応の文字列照合のための標準アルゴリズムです。UCAは、各文字に「照合要素(Collation Element: CE)」と呼ばれる数値の重みを割り当てることで、言語に依存しない一貫したソート順序を提供します。

  • 照合要素(CE): 各文字または文字シーケンスに割り当てられる重み。通常、プライマリ(Primary)、セカンダリ(Secondary)、ターシャリ(Tertiary)の3つのレベルの重みで構成されます。
    • プライマリ重み: 文字の基本的な形状やアルファベット順を決定します。例えば、'a'と'b'は異なるプライマリ重みを持ちます。
    • セカンダリ重み: アクセント記号やダイアクリティカルマーク(例: 'a'と'ä')の違いを区別します。
    • ターシャリ重み: 大文字と小文字、または幅の違い(全角/半角)を区別します。
    • クォータナリ重み: 可変要素の処理に使用されることがあります。

4. Common Locale Data Repository (CLDR)

CLDRは、Unicode Consortiumが提供する、ロケール(言語と地域)に関するデータのリポジトリです。日付、時刻、通貨の書式、数値の書式、言語名、国名、そして照合ルールなど、国際化(i18n)と地域化(l10n)に必要な情報が網羅されています。CLDRの照合ルールはUCAに基づいていますが、特定のロケールに合わせたカスタマイズ(テーラリング)が施されています。

5. Locale Data Markup Language (LDML)

LDMLは、CLDRのデータを表現するためのXMLベースのマークアップ言語です。CLDRの照合ルールもLDML形式で記述されており、特定の文字の重みの変更や、文字の挿入、削除、拡張などのテーラリングが定義されます。

6. テーラリング(Tailoring)

テーラリングとは、UCAのデフォルトの照合ルールを、特定の言語や地域の慣習に合わせてカスタマイズすることです。例えば、ドイツ語では'ä'が'a'と'b'の間にソートされるのではなく、'a'の後にソートされるといったルールがあります。LDMLでは、<collation>要素内に<rule>要素などを用いてテーラリングが記述されます。

  • アンカー(Anchor): テーラリングにおいて、新しい照合要素を挿入する際の基準となる既存の要素。LDMLでは、<reset>要素で指定されます。例えば、<reset value="z"/>は「'z'の後に挿入する」ことを意味します。
  • 可変要素(Variable Collation Elements): UCAでは、句読点、記号、スペースなどの一部の文字を「可変要素」として扱います。これらの要素は、照合の強度(Strength)設定に応じて、プライマリレベルでの比較時に無視されたり、特別な重みが割り当てられたりします。VariableTopは、可変要素のプライマリ重みの上限を定義する値です。この値より小さいプライマリ重みを持つ要素は可変要素と見なされます。
  • 拡張(Extend): LDMLのテーラリングにおいて、ある文字の照合要素に別の文字の照合要素を「拡張」して追加する概念です。例えば、<a><extend>b</extend></a>は、'a'の照合要素に'b'の照合要素を追加することを意味し、結果として「ab」としてソートされるような効果をもたらします。

7. Go言語のexp/locale/collateパッケージ

このパッケージは、Go言語でUCAとCLDRに基づいた文字列照合機能を提供する実験的なライブラリです。Builderは照合テーブルを構築するための主要な構造体であり、このコミットでそのAPIが大幅に改善されました。

技術的詳細

このコミットは、exp/locale/collateパッケージの内部構造とAPIにいくつかの重要な変更を加えています。

1. BuilderTailoringの役割分担

  • Builder: 以前はルートテーブルとテーラリングの両方を扱っていましたが、この変更により、Builderルート照合テーブルの構築に特化しました。これは主にUCAのallkeys*ファイル(文字と照合要素の重みのマッピング)からデータを読み込み、基本となる照合テーブルを生成する役割を担います。
  • Tailoring: 新たに導入された構造体で、既存の照合テーブル(通常はルートテーブル)に対するロケール固有のテーラリングを構築する役割を担います。これはCLDRのLDML XMLファイルで定義されるカスタマイズを反映するためのものです。これにより、重みベースのテーブル構築とLDMLベースのテーラリング構築の機能が明確に分離され、コードのモジュール性と理解しやすさが向上しました。

2. Builder.Addメソッドの変更

  • Add(str []rune, colelems [][]int, variables []int): 以前はvariables引数がありませんでしたが、この変更で追加されました。
    • str: 照合要素を割り当てるルーン(文字)のスライス。
    • colelems: strに対応する照合要素のシーケンス。各要素はプライマリ、セカンダリ、ターシャリなどの重みのスライスです。
    • variables: colelems内のどの照合要素が「可変要素」であるかを示すインデックスのリスト。
  • 可変要素の自動処理: Builder.Addメソッド内で、variables引数に基づいて可変要素のチェックと処理が自動的に行われるようになりました。具体的には、minNonVar(最小の非可変要素のプライマリ重み)とvarTop(最大の可変要素のプライマリ重み)がBuilder内部で追跡され、整合性チェックが行われます。これにより、VariableTopの値がBuildメソッドによって適切に生成されるようになりました。

3. テーラリング追加APIの変更 (SetAnchorInsertの導入)

  • Tailoring.SetAnchor(anchor string): テーラリングにおいて、新しい要素を挿入する基準となる「アンカー」を設定します。これはLDMLの<reset>要素に相当します。例えば、SetAnchor("z")は「'z'の後に挿入する」ことを意味します。特殊なアンカー(例: <first_tertiary_ignorable/>)もサポートされる予定です。
  • Tailoring.SetAnchorBefore(anchor string): SetAnchorと同様ですが、アンカーの「前に」要素を挿入します。
  • Tailoring.Insert(level collate.Level, str, extend string): SetAnchorで設定されたアンカーに対して、新しい照合要素を挿入します。
    • level: 挿入する照合のレベル(Primary, Secondary, Tertiaryなど)。
    • str: 挿入する文字列。
    • extend: LDMLのextend要素に対応する文字列。これが非空の場合、strの照合要素にextendの照合要素が追加されます。これにより、anchor + extendの後にstrがソートされるのと同等の効果が得られます。
  • LDML構造の反映: 以前の単一のAddTailoring呼び出しから、SetAnchorInsertの2段階の呼び出しに分離されたことで、LDMLのテーラリング定義(アンカーを設定し、その後に要素を挿入する)がより直接的にAPIに反映されるようになりました。これにより、クライアントコードの記述がLDMLのセマンティクスに近づき、ライブラリ内部の実装も簡素化されました。

4. Collatortable構造体の変更

  • collate.Collator構造体からvariableTopフィールドが削除され、代わりにcollate.table構造体(照合テーブルの内部表現)にvariableTopが移動しました。これにより、Collatorインスタンスが参照するテーブルデータの一部としてvariableTopが管理されるようになり、データの一貫性が向上しました。
  • collate.Init関数(BuilderCollatorインスタンスを生成する際に使用)も、新しいtableInitializerインターフェースを通じてvariableTopを取得するように変更されました。

5. maketables.goの変更

  • parseUCA関数がBuilder.Addを呼び出す際に、可変要素のインデックスリストを渡すように変更されました。これにより、可変要素の検出とVariableTopの計算がBuilder内部で行われるようになりました。
  • failonerror関数がfailOnErrorにリネームされ、Goの慣習に合わせた命名になりました。

これらの変更により、exp/locale/collateパッケージは、CLDRの複雑な照合データ、特にテーラリングと可変要素の処理を、より正確かつ効率的に行えるようになりました。

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

主要な変更は以下のファイルに集中しています。

  • src/pkg/exp/locale/collate/build/builder.go: Builder構造体とAddメソッドの変更、Tailoring構造体とSetAnchor, Insertメソッドの追加。
  • src/pkg/exp/locale/collate/build/table.go: table構造体にvariableTopフィールドの追加。
  • src/pkg/exp/locale/collate/collate.go: Collator構造体からvariableTopフィールドの削除と、t.variableTopへの参照変更。
  • src/pkg/exp/locale/collate/export.go: Init関数でのvariableTopの取得方法の変更。
  • src/pkg/exp/locale/collate/maketables.go: parseUCA関数でのBuilder.Add呼び出しの変更と、Builder.Buildの引数変更。

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

--- a/src/pkg/exp/locale/collate/build/builder.go
+++ b/src/pkg/exp/locale/collate/build/builder.go
@@ -60,18 +65,30 @@ func (e *entry) contractionStarter() bool {
 	return e.contractionHandle.n != 0
 }
 
-// A Builder builds collation tables.  It can generate both the root table and
-// locale-specific tables defined as tailorings to the root table.\n// The typical use case is to specify the data for the root table and all locale-specific\n// tables using Add and AddTailoring before making any call to Build.  This allows\n// Builder to ensure that a root table can support tailorings for each locale.\n+type Builder struct {
+	index    *trieBuilder
+	locale   []*Tailoring
+	entryMap map[string]*entry
+	entry    []*entry
+	t        *table
+	err      error
+	built    bool
+
+	minNonVar int // lowest primary recorded for a variable
+	varTop    int // highest primary recorded for a non-variable
+}
+
+// A Tailoring builds a collation table based on another collation table.
+// The table is defined by specifying tailorings to the underlying table.
+// See http://unicode.org/reports/tr35/ for an overview of tailoring
+// collation tables.  The CLDR contains pre-defined tailorings for a variety
+// of languages (See http://www.unicode.org/Public/cldr/2.0.1/core.zip.)
+type Tailoring struct {
+	id string
+	// TODO: implement.
+}
+
 // NewBuilder returns a new Builder.
@@ -83,14 +100,26 @@ func NewBuilder() *Builder {
 	return b
 }
 
-// Add adds an entry for the root collation element table, mapping \n+// Tailoring returns a Tailoring for the given locale.  One should \n+// have completed all calls to Add before calling Tailoring.\n+func (b *Builder) Tailoring(locale string) *Tailoring {
+	t := &Tailoring{
+		id: locale,
+	}
+	b.locale = append(b.locale, t)
+	return t
+}
+
+// Add adds an entry to the collation element table, mapping \n // a slice of runes to a sequence of collation elements.\n // A collation element is specified as list of weights: []int{primary, secondary, ...}.\n // The entries are typically obtained from a collation element table\n // as defined in http://www.unicode.org/reports/tr10/#Data_Table_Format.\n // Note that the collation elements specified by colelems are only used\n // as a guide.  The actual weights generated by Builder may differ.\n-func (b *Builder) Add(str []rune, colelems [][]int) error {\n+// The argument variables is a list of indices into colelems that should contain\n+// a value for each colelem that is a variable. (See the reference above.)\n+func (b *Builder) Add(str []rune, colelems [][]int, variables []int) error {
 	e := &entry{
 		runes: make([]rune, len(str)),
 		elems: make([][]int, len(colelems)),
@@ -113,6 +142,29 @@ func (b *Builder) Add(str []rune, colelems [][]int) error {
 			e.elems[i] = append(e.elems[i], ce[0])
 		}
 	}
+	for i, ce := range e.elems {
+		isvar := false
+		for _, j := range variables {
+			if i == j {
+				isvar = true
+			}
+		}
+		if isvar {
+			if ce[0] >= b.minNonVar && b.minNonVar > 0 {
+				return fmt.Errorf("primary value %X of variable is larger than the smallest non-variable %X", ce[0], b.minNonVar)
+			}
+			if ce[0] > b.varTop {
+				b.varTop = ce[0]
+			}
+		} else if ce[0] > 0 {
+			if ce[0] <= b.varTop {
+				return fmt.Errorf("primary value %X of non-variable is smaller than the highest variable %X", ce[0], b.varTop)
+			}
+			if b.minNonVar == 0 || ce[0] < b.minNonVar {
+				b.minNonVar = ce[0]
+			}
+		}
+	}
 	elems, err := convertLargeWeights(e.elems)
 	if err != nil {
 		return err
@@ -123,13 +175,57 @@ func (b *Builder) Add(str []rune, colelems [][]int) error {
 	return nil
 }
 
-// AddTailoring defines a tailoring x <_level y for the given locale.\n-// For example, AddTailoring(\"se\", \"z\", \"ä\", Primary) sorts \"ä\" after \"z\"\n-// at the primary level for Swedish.  AddTailoring(\"de\", \"ue\", \"ü\", Secondary)\n-// sorts \"ü\" after \"ue\" at the secondary level for German.\n+// SetAnchor sets the point after which elements passed in subsequent calls to
+// Insert will be inserted.  It is equivalent to the reset directive in an LDML
+// specification.  See Insert for an example.
+// SetAnchor supports the following logical reset positions:
+// <first_tertiary_ignorable/>, <last_teriary_ignorable/>, <first_primary_ignorable/>,
+// and <last_non_ignorable/>.
+func (t *Tailoring) SetAnchor(anchor string) error {
+	// TODO: implement.
+	return nil
+}
+
+// SetAnchorBefore is similar to SetAnchor, except that subsequent calls to
+// Insert will insert entries before the anchor.
+func (t *Tailoring) SetAnchorBefore(anchor string) error {
+	// TODO: implement.
+	return nil
+}
+
+// Insert sets the ordering of str relative to the entry set by the previous
+// call to SetAnchor or Insert.  The argument extend corresponds
+// to the extend elements as defined in LDML.  A non-empty value for extend
+// will cause the collation elements corresponding to extend to be appended
+// to the collation elements generated for the entry added by Insert.
+// This has the same net effect as sorting str after the string anchor+extend.
 // See http://www.unicode.org/reports/tr10/#Tailoring_Example for details
-// on parametric tailoring.\n-func (b *Builder) AddTailoring(locale, x, y string, l collate.Level) error {\n+// on parametric tailoring and http://unicode.org/reports/tr35/#Collation_Elements
+// for full details on LDML.
+// 
+// Examples: create a tailoring for Swedish, where "ä" is ordered after "z"
+// at the primary sorting level:
+//      t := b.Tailoring("se")
+// 	t.SetAnchor("z")
+// 	t.Insert(collate.Primary, "ä", "")
+// Order "ü" after "ue" at the secondary sorting level:
+// 	t.SetAnchor("ue")
+// 	t.Insert(collate.Secondary, "ü","")
+// or
+// 	t.SetAnchor("u")
+// 	t.Insert(collate.Secondary, "ü", "e")
+// Order "q" afer "ab" at the secondary level and "Q" after "q"
+// at the tertiary level:
+// 	t.SetAnchor("ab")
+// 	t.Insert(collate.Secondary, "q", "")
+// 	t.Insert(collate.Tertiary, "Q", "")
+// Order "b" before "a":
+//      t.SetAnchorBefore("a")
+//      t.Insert(collate.Primary, "b", "")
+// Order "0" after the last primary ignorable:
+//      t.SetAnchor("<last_primary_ignorable/>")
+//      t.Insert(collate.Primary, "0", "")
+func (t *Tailoring) Insert(level collate.Level, str, extend string) error {
 	// TODO: implement.
 	return nil
 }
@@ -189,7 +285,10 @@ func (b *Builder) error(e error) {\n func (b *Builder) build() (*table, error) {\n 	if !b.built {\n 		b.built = true
-\t\tb.t = &table{}\n+\t\tb.t = &table{\n+\t\t\tmaxContractLen: utf8.UTFMax,\n+\t\t\tvariableTop:    uint32(b.varTop),\n+\t\t}\n \n 		b.simplify()\n 		b.processExpansions()   // requires simplify
@@ -202,18 +301,23 @@ func (b *Builder) build() (*table, error) {\n 	return b.t, nil
 }
 
-// Build builds a Collator for the given locale.  To build the root table, set locale to "".\n-func (b *Builder) Build(locale string) (*collate.Collator, error) {\n+// Build builds the root Collator.\n+func (b *Builder) Build() (*collate.Collator, error) {
 	t, err := b.build()
 	if err != nil {
 		return nil, err
 	}
-\t// TODO: support multiple locales\n 	return collate.Init(t), nil
 }
 
-// Print prints all tables to a Go file that can be included in\n-// the Collate package.\n+// Build builds a Collator for Tailoring t.\n+func (t *Tailoring) Build() (*collate.Collator, error) {
+	// TODO: implement.
+	return nil, nil
+}
+
+// Print prints the tables for b and all its Tailorings as a Go file
+// that can be included in the Collate package.
 func (b *Builder) Print(w io.Writer) (int, error) {
 	t, err := b.build()
 	if err != nil {

コアとなるコードの解説

Builder構造体とAddメソッドの変更

  • Builder構造体にminNonVarvarTopという新しいフィールドが追加されました。これらは、可変要素のプライマリ重みの範囲を追跡するために使用されます。
  • Addメソッドのシグネチャがfunc (b *Builder) Add(str []rune, colelems [][]int, variables []int) errorに変更されました。
    • variables引数は、colelems内のどの要素が可変要素であるかを示すインデックスのリストです。
    • Addメソッドの内部で、variablesリストに基づいて各照合要素が可変要素であるかどうかがチェックされます。
    • 可変要素と非可変要素のプライマリ重みがminNonVarvarTopと比較され、UCAのルールに違反しないか(例: 可変要素のプライマリ重みが非可変要素の最小プライマリ重みより大きい、または非可変要素のプライマリ重みが可変要素の最大プライマリ重みより小さい)が検証されます。これにより、VariableTopの適切な設定が保証されます。

Tailoring構造体の導入と関連メソッド

  • Tailoringという新しい構造体が定義されました。これは、特定のロケールに対するテーラリングを構築するためのものです。
  • Builder.Tailoring(locale string) *Tailoringメソッドが追加され、指定されたロケールに対応するTailoringインスタンスを取得できるようになりました。
  • Tailoring.SetAnchor(anchor string) errorTailoring.SetAnchorBefore(anchor string) errorメソッドが追加されました。これらは、テーラリングにおいて新しい要素を挿入する基準点(アンカー)を設定するために使用されます。LDMLの<reset>要素のセマンティクスを反映しています。
  • Tailoring.Insert(level collate.Level, str, extend string) errorメソッドが追加されました。これは、SetAnchorで設定されたアンカーに対して、指定されたレベルで文字列strを挿入します。extend引数はLDMLのextend要素に対応し、strの照合要素にextendの照合要素を追加する効果を持ちます。

Builder.Buildメソッドの変更

  • func (b *Builder) Build() (*collate.Collator, error)のように、引数からlocale stringが削除されました。これは、Builderがルートテーブルの構築に特化し、テーラリングはTailoring構造体とそのBuildメソッド(Tailoring.Build() (*collate.Collator, error))で行われるようになったためです。
  • Builder.build()内部で、table構造体の初期化時にvariableTopb.varTopから設定されるようになりました。

collate.Collatorcollate.tableの変更

  • collate.Collator構造体からvariableTopフィールドが削除されました。
  • collate.table構造体にvariableTop uint32フィールドが追加されました。これにより、variableTopの値が照合テーブル自体に紐付けられるようになり、Collatorはテーブルからこの値を取得するようになりました。
  • collate.Init関数(Collatorを初期化する内部関数)が、新しいtableInitializerインターフェースを通じてvariableTopを取得するように変更されました。

これらの変更により、照合テーブルの構築プロセスがより構造化され、特にCLDRの複雑なテーラリングルールを扱う際の柔軟性と正確性が向上しました。

関連リンク

参考にした情報源リンク