識別子が数字で始まらないのはなぜですか?


32

ほとんどのプログラミング言語は、数字で始まる識別子を宣言できないように設計されているようです。理由を知りたいだけです。私はすでにウェブを検索しましたが、満足のいく説明を見つけることができませんでした。


4
明快さと読みやすさのメリットがある変数名の例はありますか?
セキュア

5
@Secure:3dspline、4seasonPizza、2pdfConverter、8bitInt、...
ユーザー不明

6
Forthはそれを許可します。ビルトイン:2DUP、2DROP、2SWAP、2> R、2R @、2R>、0 =など
ピーターモーテンセン

TCLを行いますが、私は考えていないように、標準TCLコマンドのいずれかが数字で始まる
JKを。

回答:


51

C / C ++では、文字の後に続く数字は数値定数と見なされ、その後に続く文字列は定数の型を修飾します。したがって、たとえば(これらはVC ++であり、それらがどの程度標準であるかはわかりません):

  • 0-符号付き整数
  • 0l-符号付き長整数
  • 0u-符号なし整数
  • 0i64-64ビット符号付き整数

したがって、a)Danielが言ったようにレクサーにとっては簡単ですが、b)0yは変数かもしれませんが0uは決してないので、明示的に区別します。さらに、「i64」などのその他の修飾子が「l」または「u」よりもずっと後に追加され、必要に応じて追加するオプションを開いたままにしておきたいと考えています。


7
また、16進数は0xd +の形式で記述されます。d+は16進数0〜fの1桁です。したがって、0xbeefは完全に有効な「数値」です。
tcrosley

20
あなたは私が言語仕様に向かわなかったことに気づいていますが、要点を説明するためにいくつかの例を提供しましたよね?
DXM

6
Re:「必要に応じてさらに追加するオプションを開いたままにしておきたい」:また、C ++ 11では独自のオプションを追加することもできます。http://en.wikipedia.org/wiki/C++11#User-defined_literalsを参照してください
-ruakh

2
これは正しい説明だとは思わない。「識別子を数字で始めることはできません」というルールは、Algol、Pascal、および数値定数のアルファベットの接尾辞を許可しない他の言語に当てはまりました。
ラリーグリッツ

1
@LarryGritz:「スペースで単語を一貫して分離することは、10世紀の広告に関する一般的な習慣となり、FORTRANが練習を放棄した1957年頃まで続きました。」—Sun FORTRANリファレンスマニュアル(wikiから)。Fortranは、一般にスペースがオプションであると判断したため、独自の特別な理由がありました。空白のような現代言語。あなたはアルゴルと独りでいるが、私はそれがどれほど現代的でもない。一方、C / C ++ / C#/ F#にはすべて接尾辞が付いています。
DXM

49

レクサーを実装する人々の利便性。(いいえ、真剣に、それについてです。さまざまな言語には他の理由がありますが、最終的にはそれが原因です。)


2
整数リテラルと、PEGまたはその他の最新の解析手法を使用した数字で始まる識別子を簡単に区別できます。プリミティブレクサーを使用するコンパイラでさえ、同じトークンカテゴリに入れて後で区別することができます。たとえば0fluリテラルであり0gluローカル識別子である場合、それは非常に厄介です。
ダニエルルバロフ

2
人々がそれらを区別することは絶対に可能です。決定は、技術的な要件ではなく、利便性(または慈善活動が少ない場合は怠)に基づいて行われます。
ダニエルピットマン

2
@DanielPittman:どんな種類の信頼できる曖昧性除去を行うには意味解析が必要なので、レクサーではできません。レクサーから決定をプッシュすると、パーサーはより複雑になり、どのようなメリットがありますか?非常に貧しいコスト/利益状況のほかに、などのケース処理するだけで良い方法はありませんint 0u = 5; unsigned int x = 0u;あなたは(おそらくX == 0またはX == 5のいずれか)。このコードの解釈を定義することを選択しかし、人々は混乱しようとしているがあいまいさのため。この方法でコンパイラを実装することは簡単であったとしても、優れた設計者はおそらくそれを行わないでしょう。
ジョレン

10
主な利便性は、言語の作成者ではなく、私の頭の中のパーサーにあります。
CodesInChaos

2
多くの人にとって、字句解析は通常、コンパイラ/インタープリターの最も遅い段階の大きな要因であるということを知ることは、まだ驚きです。
ヒッピートレイル

20

次の2つのケースを考慮してください。

事例1

識別子が数字で始まると仮定しましょう。

したがって、次のようなステートメントは有効です(識別子には1文字以上を含めることができるため)。

int 3;

上記の変数をプログラムで使用しようとすると、コンパイラーのあいまいさが生じます。

int 3、a;
3 = 5;
a = 3;

ステートメントでa=3は、3の役割は何ですか(値5の変数ですか、それとも数値3です)?

事例2

上記の例とは対照的に、言語は、数字で始まる識別子を実際に許可する一方で、数字を識別子として使用することをまだ許可しないと仮定します。これにより、次の問題が発生する可能性があります。

  • 変数が1つ以上の文字で構成できると言う変数に関する言語規則は、次のような複雑な規則に再定義する必要があります。数字(など)で始まる場合、1文字の長さにすることはできません。

  • コンパイラは、すべての数字(333など)と有効なアルファベットの接尾辞(34Lなど)が変数名として使用されている場合、エラーをチェックして報告する必要があります。変数を宣言せずにオンザフライで使用できるPythonやJSなどの緩やかに型付けされた言語if (33==5)では、すべての数字に関連する特殊なケースをチェックすることさえできない場合があります。たとえば、33はユーザーが宣言した誤った未宣言変数です ただし、コンパイラはこれを識別してエラーを報告することはできません。

この制限を行うと、プログラマーは識別子名として数字を使用できなくなります。


2
このロジックでは、キーワードにあいまいになるため、識別子に文字を含めることはできません。どれほど悲惨なint char = floatことが起こるか想像できますか?
パブ

4
@Pubby:まだ理解できていない全く意味のないことに対して、私が言ったことをどのように外挿できるかわかりません。あなたのコメントはどういう意味ですか?
aml90

私はあなたが文字通りあまりにも質問を取っていると言って、字句解析の優先順位を使用することによってそれはまったく曖昧ではないことを言っています。たとえば、コンパイラintは識別子ではなくキーワードをどのように認識していますか?まあ、int数字の語彙素が持つように、より高い優先順位を持っています。
Pubby

@Pubby:あいまいさにより、変数名を使用しているコンテキストをコンパイラが認識しないことを意味しました(字句優先順位を使用している場合でも)。たとえば、次のコードを検討し int 3,a; 3=5; a=3; てください。ステートメントa = 3で、3は識別子または数字として解釈されますか?これによりあいまいさが生じます。それが明確であることを願っています。
aml90

2
私もこの議論が弱いと思う。数字で始まるが完全に構成されていない識別子を受け入れるレクサーを作成するのは簡単です。
ラリーグリッツ

11

ほとんどの場合、これはコンパイラの作成者と解析の効率を容易にすることとは関係ありませんが、読みやすく明確なコードを促進する構文の設計と関係があります。

その言語設計者は、数字1のような数値リテラルを単なるプレーン1として書くことができたらいいと思っていました。

ナンバーワンのためのnumbericリテラルは次のようにエンコードされたように、数値リテラルは、たとえば、tildasためのいくつかの方法で引用された言語の構文設計することは十分可能だろう〜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;

コンパイルして実行する有効なコードです。


Cは、Unix用のポータブルアセンブリとして設計されました。Unixはもともと18ビットマシン用に設計されたもので、8/16/32ビットマシン値の印刷にhexが適しているのと同じように、8進数が印刷に適しています。したがって、実際には8進数が必要でした。

また、ビット調整(OR、XOR、AND、NOT)およびデバイスドライバーの実装では、リテラルと値の正確なサイズを指定することが重要です。
ジェームズアンダーソン

10

Fortranは、後の言語の設計方法に大きな影響を与えました。早い段階で(これらの問題の一部は修正されました)Fortranには、識別子に付ける名前を制限するルールがほとんどありませんでした。これにより、コンパイラーとプログラマーの両方の言語の解析が非常に難しくなりました。以下に典型的な例を示します。

if if .eq. then then = else else else = endif endif
K  I   K   K    I      I    K    I      I     K

ここでは、「言語のキーワード」にKと識別子(変数名)Iを付けました。スペルに違いがないことを考えると、これがいかに混乱しやすいかをおそらく理解できると思います。もちろん、これは極端な例であり、意図的にこのようなコードを書いた人はいないでしょう。しかし、人々言語のキーワードを識別子名として「リサイクル」することがありました。多くの場合、単純なタイプミスにより、まったく意図されていなかったにもかかわらず、言語仕様がこの方法で解析されるコードが生成されることがありました。別の有名な例については、これを比較してください:

do 10 i = 1,10

これに:

do 10 i = 1.10

1つはdoループです。コードのブロックを10回繰り返します。ただし、2番目はコンマが小数点に変更されている1.10ため、という名前の変数に値を割り当てていますdo 10 i

これは、Fortranパーサーの記述が比較的困難であることも意味しました。do行の終わりに到達するまで行の先頭が本当にキーワードであることを確信できず、aの他のすべての要素を検証しました。doループが存在しました。パーサーは一般に「バックトラック」する準備ができていて、最初から行を再解析して、実際に存在するものの「正しい」(ただし、意図しない)答えに到達する必要がありました。

数年後、言語設計者(とにかくほとんど)は反対の極端な方向に進みました-ユーザーあまり文句を言うことなく、言語に関するほぼすべてを可能な限り制限しました。

たとえば、初期のBASICは基本的に、キーワードを識別子の一部として使用することすらできないと言っていました。たとえば、(つまり、ループではなく、割り当ての始まりfora=1としてfor a = 1)解析されます。それは明らかに長続きしないほど十分な苦情を生み出しました。数字で識別子を開始することに関するルールは、多くの苦情を発生させていないようであるため、(少なくともほとんどの言語で)引き続き使用されています。for


私見これは本当の理由に最も近い。などのFortran早けれ言語は、いくつかの点であった、あまりにも正確に視覚的にソースコードを解析するために、人間のための堅牢なコンパイラや困難を書く難しさにつながる、構造化されていません。「do10i = ...」は古典的で有名な例です。言語が進化するにつれて、いくつかのルールが強化されました。アルゴルは、おそらく「識別子は文字で始まり、その後文字または数字を持つことができる」という経験則の祖父です。
ラリーグリッツ

参考までに、BASIC(Applesoft BasicおよびCommodore Basicを含む)の最も人気のあるマイクロコンピューターバージョンの基礎を形成したMicrosoft BASICインタープリターは、貪欲なトークナイザーを使用して、言語トークンに一致した文字シーケンスを高ビットセットのバイト値に変換しました。これは、構文解析なしで行われました。次に、プログラムを実行すると、インタープリターは変数名の一部を構成する文字を検出します。
-supercat

1

初期のマシンでは、字句解析を含むコンパイラ全体が数kWordで実行する必要があり、現在のモバイルデバイスの第1レベルのプロセッサデータキャッシュよりも少ないメモリであったため、この規則は非常に初期の歴史的な言語設計の決定から発展したようです。そのため、許可される変数名は非常に限られており、ごく少数のopコードで数値定数と簡単に区別できる必要がありました。

したがって、慣習はプログラマーの世代が慣れているものになりました。


1

これは、プログラミング言語に論理的に必要な規則ではなく、多くの言語設計者が使用する規則にすぎません。

識別子にすべての文字を使用できる根本的に異なる言語を設計できます。すべてのコード行について、最初の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);

それで全部です。それは無意味であり、識別子に番号を付けないというルールも論理的には無意味です。


0

「レクサーの利便性」に加えて、「読者の利便性」も考慮する価値があると思います。

コードを読むとき、どの単語が識別子であり、どの単語が数字であるかを迅速かつ繰り返し識別する必要があります。視覚的なパターンマッチングでは、最初に数字を探す方が簡単です。確認するためにすべてのキャラクターを慎重にチェックする必要がある場合は、面倒です。


0

この質問に対する答えは、正規表現を定義するオートマトンまたはより正確には有限オートマトンにあります。ルールは...コンパイラは、解析するすべての文字で決定するための正確なアルゴリズムまたはルールを必要とします。識別子が数字で始まることを許可されていた場合、コンパイラは修正中です....トークンの性質について...それは数字または識別子になります...コンパイラが以前の位置に戻ることができないため。 .so ..次のトークンが正確に識別子または数字であることをコンパイラーに明らかにするために...この制限があります...これの...コンパイラーは、次のトークンを最初の文字をスキャンするだけで知っています識別子または番号です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.