ほとんどのプログラミング言語は、数字で始まる識別子を宣言できないように設計されているようです。その理由を知りたくてたまらなかった。私はすでにウェブを検索しましたが、満足のいく説明が見つかりませんでした。
C/C++では、文字の後に続く数値は数値定数と見なされ、その後に続く文字列は定数の型を修飾します。したがって、たとえば(これらはVC++ですが、どれほど標準的かはわかりません):
したがって、a)ダニエルが言ったようにレクサーにとっては簡単ですが、b)0y mightは変数ですが、0uは決してそうではないので、明確に区別します。さらに、「i64」などの他の修飾子は「l」または「u」よりも後に追加され、必要に応じてさらに追加するオプションを開いたままにしたい。
レクサーを実装する人々の利便性。 (いいえ、真剣に、それはそれについてです。さまざまな言語には他の理由がありますが、最終的にはそれが原因です。)
次の2つのケースを検討してください。
識別子が数字で始まると仮定しましょう。
したがって、次のようなステートメントは有効です(識別子には1つ以上の文字を含めることができるため)。
int 3;
上記の変数をプログラムで使用しようとすると、コンパイラーがあいまいになります。
int 3、a;
3 = 5;
a = 3;
ステートメントa=3
3の役割は何ですか(値5の変数ですか、それとも数値3ですか?)
上記の例とは対照的に、言語が数字で始まる識別子を実際に許可し、識別子として使用される数字を許可しないと仮定しましょう。これにより、次の問題が発生する可能性があります。
変数は1つ以上の文字で構成できるという変数に関する言語規則は、次のような複雑な規則に再定義する必要があります。変数は1つ以上の文字を持つことができます数字で始まらない場合は一意である必要がありますが、数字で始まる場合は1文字の長さにすることはできません(など)
コンパイラーは、すべての数字(例:333)と有効なアルファベットの接尾辞(例:34L)が変数名として使用されている場合、エラーケースをチェックして報告する必要があります。 PythonおよびJSのような緩やかに型付けされた言語で、宣言せずにオンザフライで変数を使用できる場合、if (33==5)
のようにすべての数値が関係する特殊なケースをチェックすることさえ不可能かもしれません。ここで、33はユーザーが宣言した誤った未宣言の変数である可能性がありますが、コンパイラーはこれを識別してエラーを報告することができません。
この制限を行うと、プログラマーがID名として数値を使用できなくなります。
ほとんどの場合、これはコンパイラの作成者が簡単にして効率よく解析できるようにすることとは関係ありませんが、より明確で読みやすく明確なコードを促進する構文を設計することと関係があります。
その言語設計者は、数値1のような数値リテラルを単純な1として記述できるようになればいいと考えました。
数値リテラルがティルダなどの何らかの方法で引用されている言語構文を設計して、1番の数値リテラルが〜1〜としてエンコードされていて、キーワードではなく、引用符は変数名として扱われました。
したがって、次のようなステートメントをコーディングできます。
1 = ~2~
two = 1 * ~2~
だけでなく:
2 = ~3~
six = 2 + 2
曖昧でコードの追跡が難しい構文を選択するのは避けられません。
C言語と、Cに由来するほとんどの「中かっこ」言語も、プログラマが8進数と16進数のリテラルを直接コーディングできるようにし、これが重要な場合はリテラルのタイプを指定することをお勧めします。そう
010 // Octal 10 = 8;
0x10 // Hexadecimal 10 = 16;
5l // long integer with decimal value 5
2.0d // double float with value 2
したがって、変数名が数字で始まり、その後に少なくとも1文字を含む数字と文字の組み合わせが続くことを許可したとしても、特定のグループが変数名と数値リテラルのどちらを形成しているかを判断するという問題をプログラマーに提示します。
2lll = 22 // OK
2ll = 2 // compiler error
このようなあいまいさは、プログラムの作成や読み取りに役立ちません。
密接に関連する実世界の例として、変数名としてキーワードを使用できることが良いアイデアだとデザイナーが考えているPL/1言語を見ることができます。
IF THEN THEN THEN = ELSE; ELSE ELSE = THEN;
IF IF THEN ELSE = IF; ELSE THEN = ELSE;
DO WHILE (WHILE = DO); END = WHILE + DO; END;
コンパイルして実行する有効なコードです。
Fortranは、後の言語の設計に大きな影響を与えました。初期には(これらの問題の一部は修正されて以来)、Fortranにはほぼ識別子に付ける名前を制限するルールがありませんでした。これにより、コンパイラとプログラマの両方にとって、言語の解析が非常に困難になりました。これが古典的な例です:
if if .eq. then then = else else else = endif endif
K I K K I I K I I K
ここでは、「言語キーワード」にKと識別子(変数名)Iを付けました。スペルに違いがないことを考えると、これがどれほど混乱するか理解できると思います。もちろん、これは極端な例であり、これほど意図的にこのようなコードを書いた人はいないでしょう。時々人々はdid識別子名として言語キーワードを「リサイクル」します-そして多くの場合、単純なタイプミスは、それがそうでなかったとしても、言語仕様がこのように解析されるべきであると言ったコードをもたらす可能性がありますまったく意図していません。別のよく知られた例として、これを比較してください:
do 10 i = 1,10
これに:
do 10 i = 1.10
1つはdoループです。コードのブロックを10回繰り返します。ただし、2番目はカンマが小数点に変更されているため、1.10
という名前の変数に値do 10 i
を割り当てています。
これは、Fortranパーサーの作成が比較的困難であることも意味していました。行の最後に到達するまで、行の先頭のdo
が本当に重要な単語であるかどうかを確認できず、 do
ループの他のすべての要素が存在しました。パーサーは一般に、「バックトラック」する準備ができていなければならず、行を最初から再解析して、実際に何があったのかについての「正しい」(しかし意図しないことが多い)答えに到達します。
この数年後、言語デザイナー(ほとんどの人はとにかく)は反対の極端に向かいました-ユーザーが文句を言わずに言語に関するほとんどすべてを可能な限り制限しますtoo多く。
たとえば、初期のBASICは基本的に、キーワードのpartを識別子として使用することさえできないと言っていました-たとえば、fora=1
はfor a = 1
(つまり、for
ループの開始、not割り当て)。それは明らかにそれが非常に長く続かなかった十分な苦情を生成しました。数字で識別子を開始するというルールは明らかに多くの不満を引き起こしていないので、それは(少なくともほとんどの言語で)引き続き使用されています。
おそらく、この規則は非常に初期の歴史的な言語設計の決定から発展したものです。初期のマシンでは、字句解析を含むコンパイラ全体が数kWordで実行する必要があり、現在のモバイルデバイスの第1レベルのプロセッサデータキャッシュよりもメモリが少ないためです。そのため、許可される変数名は非常に制限されており、非常に少数のopコードで数値定数と簡単に区別できる必要がありました。
したがって、この慣習は、何世代にもわたるプログラマーが慣れ親しんだものとなりました。
これはプログラミング言語に論理的に必要な規則ではなく、多くの言語設計者が使用する規則にすぎません。
識別子にすべての文字を使用できる根本的に異なる言語を設計できます。すべてのコード行で、最初の20文字はステートメントタイプを説明し、次の20文字はステートメントの最初のシンボルを定義し、次の20文字はステートメントのオペランドです。この言語はスタックプロセッサで実行されます。
01234567890123456789 01234567890123456789 01234567890123456789
decl symbol 12345
assign value 12345 12345
decl symbol 99999
assign value 99999 12345
Push 12345
Push 99999
add
print top
このコードは、以下のようにCで翻訳できます。
int i12345 = 12345;
int i99999 = 12345;
printf("%d", i12345+i9999);
それで全部です。それは無意味であり、識別子に番号を付けないというルールも論理的には無意味です。
この質問に対する答えは、正規表現を定義するオートマトンまたはより正確には有限オートマトンにあります。ルールは...コンパイラは、解析するすべての文字で決定する正確なアルゴリズムまたはルールを必要とします。識別子が数字で始まることを許可されていた場合、コンパイラは修正されます。トークンの性質について...数字または識別子になります...そしてコンパイラは以前の位置に戻ることができません。 .so ..次のトークンが正確に識別子または番号であることをコンパイラに明確にする...この制限があります...これのcoz ...コンパイラは、最初の文字をスキャンするだけで、次のトークンをスキャンすることでわかります識別子または番号です。
「レクサーの利便性」に加えて、「読者の利便性」も検討に値すると思います。
コードを読み取るときは、識別子である単語と数字である単語をすばやく繰り返し識別する必要があります。最初の数字を探すことは、視覚的なパターンマッチングで簡単です。すべてのキャラクターを注意深くチェックして確認する必要がある場合、それは面倒です。