いい加減、小ネタらしい小ネタを書かないとタイトル詐欺臭いのでほんとに小ネタを。
C#では、以下のようなコードが書けたりします。変数ab
を用意して、a\u200db
って変数に書き込むと、ab
の値が変わるという。
要するに、この2つは識別子としては同一扱いされます。
using System;
class Program
{
static void Main()
{
var ab = 0;
a\u200db = 1; // ab と同じ扱い。\u200d は Zero Width Joiner
Console.WriteLine(ab); // 1
}
}
この挙動を説明するには、以下の2つの仕様が出てきます。
- Unicode エスケープ シーケンス
-
識別子におけるフォーマット文字の挙動
- 参考: 2.4.2 識別子
1つのUnicodeエスケープ シーケンスは、\u
に続けて4桁の16進数を打つか、\U
に続けて8桁の16進数を打つと、その番号に対応したUnicode文字に変換されるというものです。このエスケープ シーケンスは、文字列リテラルの外、どこででも有効です。例えば、以下のようなことも可能。aの文字は、UnicodeではU+61です。
using System;
class Program
{
static void Main()
{
var a = 0;
\u0061 = 1;
Console.WriteLine(a);
}
}
もう1つは、フォーマット文字は識別子に含められるけど、除外して考えるという仕様。 フォーマット文字ってのは、文字を描画方法とかを指定するための不可視文字で、例えば以下のようなものがあります。
- Zero Width Joiner (U+200D): その左右の文字が不可分なことを表す。ゼロ幅接合子。略称 ZWJ。
- Left-to-Right Mark (U+200E): 文字を左から右に向かって描画すべきということを表す
- Right-to-Left Mark (U+200F): 同上、右から左
一部の自然言語でこの手の制御が必要だけども、見えない文字だからこの文字のあるなしで別識別子にはしたくないっていうことでしょう。
これら2つを組み合わせた結果が冒頭のコードです。a\u200db
は、a
とb
の間にZWJを挟んだ状態で、結果的に、識別子としてはZWJが無視されて、ab
として扱われます。
まあ、見えない文字とか無視すべきですよね。 普通、見えない文字はそもそも識別子として使えなくしてるものなんですが、Right-to-Left Markとかは、アラビア語プログラミングとかすると使うかもしれないですもんねぇ。「使う」というか、もしかしたらエディターによって勝手に挿入されるかもしれず。 その場合、無視すべき、ということなんでしょう。
Unicode Consortium で規定
この挙動、どうも、Unicode Consortiumのレポートに基づいてるみたいです。
Unicode Technical Report #15の、Annex7: Annex 7: Programming Language Identifiersのところ。 プログラミング言語の識別子に使える文字はどうあるべきか、みたいな話が結構詳細に書かれています。 これに沿っている言語は他にもありそうなので、試してみるといいかも。