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

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

このコミットは、Go言語のunicodeパッケージにUnicodeの10進数字(Decimal Digit)に関するデータと関連する関数を追加するものです。これは、将来的にGo言語の識別子(変数名、関数名など)にUnicodeの10進数字の使用を許可するための準備作業として行われました。

コミット

commit 4cbfcae3d83d6f7b07c5bf2bb42c9bc975c341f8
Author: Rob Pike <r@golang.org>
Date:   Tue Mar 10 16:30:27 2009 -0700

    add unicode data for decimal digit, preparatory to allowing them in identifiers.
    
    R=rsc
    DELTA=431  (430 added, 0 deleted, 1 changed)
    OCL=25975
    CL=26059

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

https://github.com/golang/go/commit/4cbfcae3d83d6f7b07c5bf2bb42c9bc975c341f8

元コミット内容

このコミットは、Go言語の標準ライブラリであるunicodeパッケージに、Unicodeの「10進数字」プロパティを持つ文字のデータと、その文字が10進数字であるかを判定するIsDecimalDigit関数を追加します。具体的には、src/lib/unicode/decimaldigit.goという新しいファイルが作成され、UnicodeのNd (Number, Decimal Digit) カテゴリに属する文字の範囲がDecimalDigitという[]Range型の変数として定義されています。また、このデータを利用して文字が10進数字であるかをチェックするIsDecimalDigit関数も実装されています。

同時に、この新しい機能のテストコードとしてsrc/lib/unicode/decimaldigit_test.goが追加され、Makefileも更新されて新しいソースファイルがビルドプロセスに含まれるようになっています。

コミットメッセージには「identifiersでそれらを許可するための準備」と明記されており、Go言語の識別子に多言語の数字を含めるための基盤整備であることが示唆されています。

変更の背景

Go言語は設計当初からUnicodeを深くサポートすることを目指していました。しかし、初期の段階では、識別子に含めることができる文字の範囲はASCII文字に限定されているか、あるいは非常に限定的なUnicode文字セットのみが考慮されていた可能性があります。

このコミットが行われた2009年3月は、Go言語がまだ一般に公開される前の開発初期段階にあたります。Go言語の設計思想の一つに「シンプルさ」と「実用性」があり、世界中の開発者が利用することを考慮すると、識別子にASCII以外の文字、特に各言語で使われる数字を含めることは自然な流れでした。

この変更の直接的な背景は、Go言語の識別子ルールを拡張し、Unicodeの10進数字を識別子の一部として許可するための準備です。これにより、例えばアラビア数字(U+0660-U+0669)やデーヴァナーガリー数字(U+0966-U+096F)など、様々な書記体系の数字をGoのコード内で変数名や関数名に利用できるようになります。これは、国際化対応(i18n)の観点から非常に重要なステップであり、Go言語がより多くの言語圏の開発者にとって使いやすいものとなるための基盤を築くものです。

前提知識の解説

Unicodeと文字プロパティ

Unicodeは、世界中のあらゆる文字を統一的に扱うための文字コード標準です。各文字には一意のコードポイントが割り当てられ、さらにその文字の特性を示す様々な「文字プロパティ」が定義されています。文字プロパティは、文字の種類(例: 大文字、小文字、数字、記号)、スクリプト(例: ラテン文字、キリル文字、アラビア文字)、用途(例: 句読点、空白文字)など、多岐にわたります。

このコミットで特に重要なのは「General Category」というプロパティです。これは文字の一般的な分類を示すもので、例えばLuは「Letter, Uppercase」(大文字)、Llは「Letter, Lowercase」(小文字)、そしてこのコミットで扱われるNdは「Number, Decimal Digit」(10進数字)を意味します。Ndプロパティを持つ文字は、特定の書記体系において10進数の数字として機能する文字です。

Go言語のunicodeパッケージ

Go言語の標準ライブラリには、Unicodeの文字プロパティや文字の分類、UTF-8エンコーディングの処理など、Unicode関連の機能を提供するunicodeパッケージが含まれています。このパッケージは、Goプログラムが多言語のテキストを正確に処理するために不可欠です。

unicodeパッケージは、Unicodeの文字プロパティに基づいて文字を分類するための関数(例: unicode.IsLetter, unicode.IsDigitなど)を提供します。これらの関数は、内部的にUnicodeのデータテーブルを参照して文字の分類を行います。

Go言語の識別子ルール

プログラミング言語における識別子(Identifier)とは、変数、関数、型、パッケージなどの名前として使用される文字列のことです。各プログラミング言語には、識別子として使用できる文字のルールが定められています。

Go言語の初期の識別子ルールは、ASCII文字のアルファベットと数字、そしてアンダースコア(_)に限定されていました。しかし、Go言語は国際化を重視しており、より多くのUnicode文字を識別子に含めることを目指していました。このコミットは、その目標に向けた重要な一歩となります。

Go言語の識別子ルールは、UnicodeのGeneral Categoryプロパティに基づいて定義されています。具体的には、識別子の最初の文字はLetterカテゴリ(L)またはアンダースコアである必要があり、それ以降の文字はLetterカテゴリ、Decimal Digitカテゴリ(Nd)、Connector Punctuationカテゴリ(Pc、アンダースコアなど)、Markカテゴリ(M)、またはNumber, Letterカテゴリ(Nl)のいずれかである必要があります。このコミットは、Ndカテゴリの文字を識別子に含めるためのデータを提供することで、このルールをサポートします。

MakefileとGoのビルドシステム

Go言語の初期のビルドシステムは、現在のようなgo buildコマンドが主流になる前は、Makefileが広く利用されていました。Makefileは、プログラムのコンパイルやリンクなどのビルドプロセスを自動化するためのツールです。このコミットでは、新しく追加されたGoソースファイル(decimaldigit.godecimaldigit_test.go)がビルドプロセスに正しく組み込まれるようにMakefileが更新されています。

技術的詳細

このコミットの技術的な核心は、UnicodeのNd(Number, Decimal Digit)カテゴリに属するすべての文字の範囲をGoのデータ構造として表現し、それらを効率的に検索できるようにすることです。

src/lib/unicode/decimaldigit.goでは、DecimalDigitという[]Range型のスライスが定義されています。Range型は、Goのunicodeパッケージで文字の範囲を表現するために使用される構造体で、Lo(範囲の開始コードポイント)、Hi(範囲の終了コードポイント)、Stride(範囲内の文字の増分)の3つのフィールドを持ちます。このコミットでは、Strideはすべて1に設定されており、連続するコードポイントが10進数字であることを示しています。

DecimalDigitスライスには、UnicodeのUnicodeData.txtファイルから抽出された、Ndプロパティを持つ文字の範囲がハードコードされています。コミットメッセージのコメントにもあるように、これは手動で生成されたものであり、将来的には自動生成されることが示唆されています。このデータは、世界中の様々な書記体系における10進数字(例: ラテン数字、アラビア数字、デーヴァナーガリー数字、タイ数字など)を網羅しています。

IsDecimalDigit関数は、与えられたルーン(Unicodeコードポイント)がDecimalDigitスライスで定義されたいずれかの範囲に含まれるかどうかをチェックすることで、そのルーンが10進数字であるかを判定します。このチェックは、unicodeパッケージ内の既存のIs関数(Is(ranges []Range, r int) bool)を利用して行われます。Is関数は、指定されたルーンが与えられた範囲のリストに含まれるかを効率的に検索するロジックを内包しています。

src/lib/unicode/decimaldigit_test.goでは、TestIsDecimalDigitというテスト関数が実装されています。このテストでは、decimalというスライスにNdプロパティを持つ既知の文字コードポイントが列挙されており、IsDecimalDigit関数がこれらの文字に対してtrueを返すことを確認します。また、letterというスライスにはNdプロパティを持たない文字コードポイントが列挙されており、IsDecimalDigit関数がこれらの文字に対してfalseを返すことを確認することで、関数の正確性を検証しています。このテストデータもUnicodeData.txtからgrepコマンドとsedコマンドを組み合わせて抽出されたものであることがコメントに記載されており、データの信頼性を高めています。

Makefileの変更は、新しいdecimaldigit.goファイルがunicode.aというアーカイブライブラリにコンパイルされ、リンクされるようにするためのものです。O2という新しい変数でdecimaldigit.$O(コンパイルされたオブジェクトファイル)が定義され、unicode.aの依存関係にa2O2をビルドするターゲット)が追加されています。これにより、decimaldigit.goがGoの標準ライブラリの一部として適切にビルドされるようになります。

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

このコミットにおけるコアとなるコードの変更は、以下の3つのファイルに集中しています。

  1. src/lib/unicode/Makefile:

    • O2変数の追加: decimaldigit.$Oを定義。
    • unicode.aターゲットの依存関係にa2を追加。
    • a2ターゲットの追加: O2をビルドし、unicode.aにアーカイブするルール。
    • $(O2): a1の追加: decimaldigit.goのビルドがletter.goのビルド後に実行されるように依存関係を設定。
  2. src/lib/unicode/decimaldigit.go: (新規ファイル)

    • package unicodeの宣言。
    • DecimalDigit変数の定義: Unicodeの10進数字のコードポイント範囲を[]Range型でハードコード。
      • 例: Range{0x0030, 0x0039, 1} (ASCII数字 0-9)
      • 例: Range{0x0660, 0x0669, 1} (アラビア数字 0-9)
      • その他、多数の多言語の10進数字範囲。
    • IsDecimalDigit関数の定義: func IsDecimalDigit(rune int) bool
      • 内部でunicode.Is(DecimalDigit, rune)を呼び出し、ルーンがDecimalDigitの範囲に含まれるかを判定。
  3. src/lib/unicode/decimaldigit_test.go: (新規ファイル)

    • package unicodeの宣言。
    • import ("testing"; "unicode")
    • decimal変数の定義: Ndプロパティを持つ既知の文字コードポイントのリスト。
    • letter変数の定義: Ndプロパティを持たない既知の文字コードポイントのリスト(テストの対照用)。
    • TestIsDecimalDigit関数の定義:
      • decimalリストの各ルーンに対してIsDecimalDigittrueを返すことをアサート。
      • letterリストの各ルーンに対してIsDecimalDigitfalseを返すことをアサート。

コアとなるコードの解説

src/lib/unicode/Makefileの変更

この変更は、新しく追加されたdecimaldigit.goファイルがGoのビルドシステムによって正しくコンパイルされ、unicodeパッケージのアーカイブファイル(unicode.a)に組み込まれるようにするためのものです。

  • O2=\\\n\tdecimaldigit.$O\\: O2という変数は、decimaldigit.goがコンパイルされた結果生成されるオブジェクトファイルの名前(例: decimaldigit.o)を定義しています。
  • unicode.a: a1 a2: unicode.aという最終的なアーカイブファイルを作成するために、a1(既存のletter.goなどから生成されるオブジェクトファイル群)とa2(新しく追加されるdecimaldigit.o)の両方が必要であることを示しています。
  • a2:\t$(O2)\n\t$(AR) grc unicode.a decimaldigit.$O\n\trm -f $(O2): a2ターゲットは、decimaldigit.oをビルドし、それをunicode.aアーカイブに追加する手順を定義しています。$(AR) grcは、Goのアーカイブツール(go tool packまたはar)を使ってオブジェクトファイルをアーカイブに追加するコマンドです。
  • $(O2): a1: この行は、decimaldigit.oをビルドする前にa1ターゲットが完了している必要があることを示しています。これは、unicode.aが段階的に構築されるプロセスの一部です。

src/lib/unicode/decimaldigit.goの新規追加

このファイルは、Go言語がUnicodeの10進数字を認識するためのデータとAPIを提供します。

  • var DecimalDigit = []Range{...}:
    • これは、UnicodeのNdカテゴリに属するすべての文字のコードポイント範囲を定義するGoのデータ構造です。
    • Range構造体は、Lo(範囲の開始コードポイント)、Hi(範囲の終了コードポイント)、Stride(範囲内の文字の増分)で構成されます。Stride: 1は、LoからHiまでのすべてのコードポイントが10進数字であることを意味します。
    • このリストには、基本的なASCII数字(0x0030-0x0039)だけでなく、アラビア数字、デーヴァナーガリー数字、タイ数字など、世界中の様々な書記体系で使われる10進数字の範囲が含まれています。これにより、Go言語が多言語の数字を正確に識別できるようになります。
  • func IsDecimalDigit(rune int) bool { return Is(DecimalDigit, rune); }:
    • この関数は、Goのunicodeパッケージの外部から呼び出される主要なAPIです。
    • 引数としてrune(GoにおけるUnicodeコードポイントを表す型)を受け取ります。
    • Is関数はunicodeパッケージの内部関数で、与えられたルーンが、指定された[]Rangeスライス(ここではDecimalDigit)で定義されたいずれかの範囲に含まれるかどうかを効率的にチェックします。
    • この関数がtrueを返せば、そのルーンはUnicodeの10進数字であると判断されます。

src/lib/unicode/decimaldigit_test.goの新規追加

このテストファイルは、IsDecimalDigit関数の正確性を保証するために非常に重要です。

  • var decimal = []int{...}:
    • このスライスには、UnicodeData.txtから抽出された、実際にNdプロパティを持つ文字のコードポイントが個別に列挙されています。これは、DecimalDigitの範囲定義が正しいことを検証するための「真の値」として機能します。
  • var letter = []int{...}:
    • このスライスには、Ndプロパティを持たないが、他の文字プロパティ(例えばLetter)を持つ文字のコードポイントが列挙されています。これは、IsDecimalDigit関数が誤って非数字を数字として分類しないことを確認するためのネガティブテストケースとして機能します。
  • func TestIsDecimalDigit(t *testing.T) { ... }:
    • このテスト関数は、decimalスライス内のすべてのルーンに対してIsDecimalDigitを呼び出し、結果がtrueであることをアサートします。もしfalseが返された場合、テストは失敗し、エラーメッセージが出力されます。
    • 同様に、letterスライス内のすべてのルーンに対してIsDecimalDigitを呼び出し、結果がfalseであることをアサートします。もしtrueが返された場合、テストは失敗します。
    • これらのテストにより、IsDecimalDigit関数がUnicodeの10進数字を正確に識別し、それ以外の文字と区別できることが保証されます。

これらの変更は、Go言語が多言語対応を強化し、将来的に識別子にUnicodeの10進数字を許可するための堅牢な基盤を築くものです。

関連リンク

参考にした情報源リンク