[インデックス 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.go
とdecimaldigit_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
の依存関係にa2
(O2
をビルドするターゲット)が追加されています。これにより、decimaldigit.go
がGoの標準ライブラリの一部として適切にビルドされるようになります。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、以下の3つのファイルに集中しています。
-
src/lib/unicode/Makefile
:O2
変数の追加:decimaldigit.$O
を定義。unicode.a
ターゲットの依存関係にa2
を追加。a2
ターゲットの追加:O2
をビルドし、unicode.a
にアーカイブするルール。$(O2): a1
の追加:decimaldigit.go
のビルドがletter.go
のビルド後に実行されるように依存関係を設定。
-
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
の範囲に含まれるかを判定。
- 内部で
-
src/lib/unicode/decimaldigit_test.go
: (新規ファイル)package unicode
の宣言。import ("testing"; "unicode")
decimal
変数の定義:Nd
プロパティを持つ既知の文字コードポイントのリスト。letter
変数の定義:Nd
プロパティを持たない既知の文字コードポイントのリスト(テストの対照用)。TestIsDecimalDigit
関数の定義:decimal
リストの各ルーンに対してIsDecimalDigit
がtrue
を返すことをアサート。letter
リストの各ルーンに対してIsDecimalDigit
がfalse
を返すことをアサート。
コアとなるコードの解説
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言語が多言語の数字を正確に識別できるようになります。
- これは、Unicodeの
func IsDecimalDigit(rune int) bool { return Is(DecimalDigit, rune); }
:- この関数は、Goの
unicode
パッケージの外部から呼び出される主要なAPIです。 - 引数として
rune
(GoにおけるUnicodeコードポイントを表す型)を受け取ります。 Is
関数はunicode
パッケージの内部関数で、与えられたルーンが、指定された[]Range
スライス(ここではDecimalDigit
)で定義されたいずれかの範囲に含まれるかどうかを効率的にチェックします。- この関数が
true
を返せば、そのルーンはUnicodeの10進数字であると判断されます。
- この関数は、Goの
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進数字を許可するための堅牢な基盤を築くものです。
関連リンク
- Go言語の
unicode
パッケージのドキュメント: https://pkg.go.dev/unicode - Unicode Consortium: https://home.unicode.org/
- Unicode Standard Annex #31: Unicode Identifier and Pattern Syntax (Go言語の識別子ルールに影響を与える可能性のあるUnicode標準): https://www.unicode.org/reports/tr31/
参考にした情報源リンク
- Go言語のソースコード (golang/go GitHubリポジトリ): https://github.com/golang/go
- Unicode Character Database (UCD) - UnicodeData.txt (文字プロパティの定義元): https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt
- Go言語の識別子に関する公式ドキュメント (Go言語仕様): https://go.dev/ref/spec#Identifiers
- Go言語の初期のビルドシステムに関する情報 (Goの歴史や設計に関する記事):
- The Go Programming Language Specification (初期の仕様): https://go.dev/doc/go1.html
- Go at Google: Language Design in the Service of Software Engineering (Rob PikeによるGoの設計思想に関する記事): https://go.dev/talks/2012/go4g.slide
- Go言語の
unicode
パッケージのRange
型に関する情報: https://pkg.go.dev/unicode#Range - Go言語の
unicode
パッケージのIs
関数に関する情報: https://pkg.go.dev/unicode#Is - Go言語の
Makefile
の利用に関する情報 (Goの初期のビルドプロセス): https://go.dev/doc/install/source (古い情報を含む可能性があるため注意) - Go言語の識別子ルールに関する議論 (GoのIssueトラッカーやメーリングリストのアーカイブ):
- Issue 500: unicode identifiers: https://github.com/golang/go/issues/500 (このコミットの後の議論も含む)
- Go Developers Mailing List Archives: https://groups.google.com/g/golang-nuts (過去の議論を検索可能)
- Rob PikeのGoに関する講演や記事: https://go.dev/talks/
- Russ CoxのGoに関する講演や記事: https://research.swtch.com/ (rscはRuss Coxのイニシャル)
- Go言語の歴史に関する情報: https://go.dev/doc/history
- Go言語の国際化に関する情報: https://go.dev/blog/go1.8-i18n (より新しいGoの国際化機能に関する記事だが、背景理解に役立つ)
- Unicode General Category "Nd" (Number, Decimal Digit) の詳細: https://www.unicode.org/reports/tr44/#General_Category_Values
- Go言語のテストフレームワーク
testing
パッケージ: https://pkg.go.dev/testing - Go言語の
rune
型: https://go.dev/blog/strings (Goにおける文字列と文字の扱い) - Go言語の
go tool pack
コマンド (初期のアーカイブツール): https://go.dev/cmd/go/#hdr-Go_tool_commands (現在のgo tool
コマンドの一部として説明されている) grep
コマンドとsed
コマンドの一般的な使い方 (テストデータ生成の背景理解のため): https://www.gnu.org/software/grep/manual/grep.html, https://www.gnu.org/software/sed/manual/sed.html