私がしばらく前に新しいC++開発者と仕事をしていたとき、「変数名が数字で始まらないのはなぜですか」という質問をしました。
一部の数字にテキスト(123456L、123456U)を含めることができることと、コンパイラーがある程度のアルファ文字を持つすべてを変数名と考えていた場合、それは不可能であることを除いて、答えを思い付くことができませんでした。
それは正しい答えでしたか?他に理由はありますか?
string 2BeOrNot2Be = "that is the question"; // Why won't this compile?
なぜなら、数字列は有効な識別子であると同時に有効な数字になるからです。
int 17 = 497;
int 42 = 6 * 9;
String 1111 = "Totally text";
これについてよく考えてください:
int 2d = 42;
double a = 2d;
なに? 2.0?または42?
ヒント、取得できない場合、数字の後のdは、その前の数字が二重リテラルであることを意味します
今では慣例ですが、技術的な要件として始まりました。
昔は、FORTRANやBASICなどの言語のパーサーはスペースを使用する必要がありませんでした。したがって、基本的には次の内容は同じです。
10 V1=100
20 PRINT V1
そして
10V1=100
20PRINTV1
ここで、数字の接頭辞が許可されたとします。これをどう解釈しますか?
101V=100
なので
10 1V = 100
またはとして
101 V = 100
またはとして
1 01V = 100
そのため、これは違法になりました。
コンパイル中の字句解析ではバックトラッキングが回避されるためです。次のような変数:
Apple;
コンパイラは、文字「A」に一致するとすぐに識別子であることを認識します。
ただし、次のような変数:
123Apple;
コンパイラは、 'a'に到達するまで数字か識別子かを判断できず、結果としてバックトラックが必要になります。
コンパイラ/パーサー/レキシカルアナライザーは、私にとってはかなり前のことでしたが、コンパイル単位の数字がリテラルを表すのか識別子を表すのかを明確に判断するのは困難だったと思います。
スペースが重要でない言語(正しく覚えていればALGOLや元のFORTRANなど)は、そのために識別子を開始するための数字を受け入れることができませんでした。
これは、ストレージまたは数値ベースを示す特別な表記の前に遡ります。
識別子を数字で始めることができると便利だと思います。識別子にアンダースコアを追加することでこの制限を回避できると1人または2人が言及していますが、それは本当に見苦しいです。
問題の一部は、0xdeadbeefなどの数字リテラルに起因しているため、数字で始まる識別子の覚えやすいルールを思い付くことは困難です。それを行う1つの方法は、キーワードまたは数値リテラルではない[A-Za-z _] +に一致するものをすべて許可することです。問題は、0xdeadporkが許可されるが、0xdeadbeefは許可されないなどの奇妙なことにつながることです。最終的に、私たちはすべての肉に公平であるべきだと思います。
私が最初にCを学んだとき、変数名の規則はarbitrary意的で制限的だったと感じたことを覚えています。最悪なことに、それらは覚えにくいので、私はそれらを学ぶことをやめました。私はちょうどいいと思ったことをやった、それはかなりうまくいった。私はもっと多くを学んだので、それほど悪くはないようで、ついに私はそれを正しく学ぶことに取り掛かりました。
トークンを解析するとき、最初の文字を見て、それが識別子またはリテラルであるかどうかを判断し、それを処理のために正しい関数に送信するだけでよいので、おそらくいくつかの理由で決定しました。これがパフォーマンスの最適化です。
他のオプションは、それがリテラルでないかどうかをチェックし、識別子のドメインをユニバースからリテラルを引いたままにすることです。しかし、これを行うには、すべてのトークンのすべての文字を調べて、それを分類する方法を知る必要があります。
また、識別子はニーモニックであると想定されているため、単語は数字よりも覚えやすいという文体的な意味合いもあります。オリジナルの言語の多くが今後数十年のスタイルを設定して書かれていたとき、「to」を「2」に置き換えることを考えていませんでした。
何人かの人々が気づいたように、変数名の有効な形式に関する多くの歴史的な手荷物があります。そして、言語デザイナーは常に、新しい言語を作成するときに知っていることに影響されます。
とはいえ、ほとんどの場合、言語が変数名を数字で始めることを許可しないのは、それらが言語設計のルールだからです。多くの場合、このような単純なルールにより、言語の解析と字句解析が非常に簡単になるためです。しかし、すべての言語設計者がこれが本当の理由だと知っているわけではありません。最新の字句解析ツールが役立ちます。これを許容範囲として定義しようとすると、解析の競合が発生するためです。
あなたの言語がヘラルド変数名に対して一意に識別可能な文字を持っている場合、それらが数字で始まるように設定することが可能です。同様の規則のバリエーションを使用して、変数名にスペースを許可することもできます。しかし、結果として得られる言語は、一般的な従来の言語にまったく似ていないと思われます。
変数が数字で始まりスペースが埋め込まれていることを許可するかなり単純なHTMLテンプレート言語の例については、 Qompose を参照してください。
変数名は、次のような問題を引き起こす可能性があるため、数字で始めることはできません。
int a = 2;
int 2 = 5;
int c = 2 * a;
cの値は何ですか? 4または10です!
もう一つの例:
float 5 = 25;
float b = 5.5;
最初の5は数字であるか、オブジェクト(。演算子)です2番目の5にも同様の問題があります。
たぶん、他のいくつかの理由があります。したがって、変数名の先頭に数字を使用しないでください。
COBOLでは、変数を数字で始めることができます。
制限は任意です。さまざまなLispでは、シンボル名を数字で始めることができます。
キーワードと識別子が数字で始まることを許可した場合、レクサー(コンパイラの一部)は数値リテラルの開始とキーワードを簡単に区別できなかったのは、ずっと複雑に(そして遅く)なったからです。
数字を使用して変数名を開始すると、コンパイル中またはインタープリテーション中のエラーチェックが非常に複雑になります。
数字のように始まった変数名の使用を許可すると、おそらく言語設計者にとって大きな問題を引き起こすでしょう。ソースコードの解析中に、コンパイラ/インタープリターが変数名が必要な数字で始まるトークンに出会うたびに、トークンが本当に変数であるかエラーであるかを判断するために、巨大で複雑なルールセットを検索する必要があります。言語パーサーに追加された複雑さが、この機能を正当化しない場合があります。
私が覚えている限り(約40年)、変数名の開始に数字を使用できる言語を使用したことはないと思います。これは少なくとも一度は行われたと確信しています。たぶん、ここの誰かが実際にどこかでこれを見ました。
言語設計者がそれをルールにしたため、C++にはできません。独自の言語を作成する場合、確かにそれを許可することができますが、おそらく彼らがした同じ問題に遭遇し、それを許可しないことに決めます。問題を引き起こす変数名の例:
0x、2d、5555
構文規則を緩和することに関する重要な問題の1つは、コーディングプロセスに認知的不協和音を導入することです。コードをどう考えるかは、これがもたらす明確さの欠如に大きく影響される可能性があります。
「ツールの最も重要な側面はユーザーへの影響だ」と言ったのはダイクストラではなかったでしょうか?
コンパイラーは、numberではなくメモリー位置でASCIIを使用して変数を識別するのは簡単です。
おそらく、それが人間が数字であるか識別子であるかを判別しやすくするためであり、伝統のためです。数字で始まる識別子があれば、字句スキャンはそれほど複雑になりません。
すべての言語で、数字で始まる識別子が禁止されているわけではありません。 Forthでは、数値にすることができ、小さな整数は通常、Forthワード(本質的に識別子)として定義されました。これは、「2」を数値として認識するよりもスタックに2をプッシュするルーチンとして「2」を読む方が速いためですその値は2でした。(プログラマまたはディスクブロックからの入力を処理する際、Forthシステムは入力をスペースに従って分割します。定義されたWordであるかどうかを辞書で調べて、そうでない場合は数値に変換しようとし、そうでない場合はエラーにフラグを立てます。)
数字で始まるシンボル名を許可したとします。ここで、変数に12345foobarという名前を付けたいとします。これを12345とどのように区別しますか?実際、正規表現を使用するのはそれほど難しくありません。問題は実際にはパフォーマンスの1つです。なぜこれが非常に詳細であるかを実際に説明することはできませんが、12345foobarを12345と区別するにはバックトラックが必要であるという事実に本質的に要約されます。これにより、正規表現が非決定的になります。
これについてのより良い説明があります ここ 。
変数の宣言に関しては、何も問題はありませんが、この変数を他の場所で使用しようとすると、あいまいさがあります。
let 1 = "Hello world!" print(1)print(1)
printは、すべてのタイプの変数を受け入れる汎用メソッドです。そのため、コンパイラは、プログラマーが参照する(1)整数値の1または文字列値を格納する1を参照しません。このような状況のコンパイラーは、そのようなものを定義できるようにする方が良いかもしれませんが、この曖昧なものを使用しようとすると、エラーを修正する方法に修正機能付きのエラーをもたらし、このあいまいさを解消します。
変数は、コンパイラーによってコンパイル時にも値と見なされる可能性があるため、値は値を繰り返し再帰的に呼び出すことができます
元々は、変数名を数字ではなく文字列として覚えやすい(より多くの意味を与えることができる)ためでしたが、文字列の意味を強化したり、同じ変数名の使用を許可するために文字列に数字を含めることができますが、別個の、しかし密接な意味または文脈を持つものとして指定します。たとえば、loop1、loop2などは、常にループ内にいること、および/またはループ2がloop1内のループであることを常に通知します。変数としてどちらを好むでしょう(より意味があります):アドレスまたは1121298?覚えやすい方はどれですか?ただし、言語がテキストまたは数字($ addressの$など)だけではないことを示すために何かを使用している場合、実際には違いはありません。この場合)。いずれにせよ、言語設計者が言語のルールとして使用するものになります。
コンパイラには次の7つのフェーズがあります。
コードのコンパイル中に字句解析フェーズでバックトラッキングが回避されます。 Appleのような変数であるコンパイラは、字句解析フェーズで文字「A」に出会うとすぐに識別子を認識します。ただし、123Appleのような変数の場合、コンパイラは「a」に到達するまで数値または識別子を決定できず、変数であることを識別するために字句解析フェーズに進むにはバックトラックが必要です。ただし、コンパイラではサポートされていません。
トークンを解析するとき、最初の文字を見て、それが識別子かリテラルかを判断し、処理のために正しい関数に送信するだけです。それがパフォーマンスの最適化です。
簡単な答えはできると思う、制限は言語ベースです。 C++や他の多くの言語では、言語がサポートしていないためできません。それを許可するルールには組み込まれていません。
問題は、なぜ王がチェスで一度に4つのスペースを移動できないのかという質問に似ていますか?チェスでは違法な動きだからです。別のゲームでそれを確認できます。それは単にプレイされているルールに依存します。
コードの一部をコンパイルするときに、字句解析フェーズでバックトラッキングが回避されます。 Appleのような変数。 、コンパイラは、字句解析フェーズで文字「A」の文字に一致するとすぐに識別子を認識します。ただし、123Appleのような変数。 、コンパイラは「a」に到達するまでその数値または識別子を判断できず、変数であることを特定するために字句解析フェーズに進むにはバックトラックが必要です。ただし、コンパイラではサポートされていません。