为什么标识符不应该以数字开头?


32

大多数编程语言似乎设计为不允许人声明以数字开头的标识符。我只是好奇地知道原因。我已经在网上搜索过,但是找不到令人满意的解释。


4
您是否有一个变量名示例,该示例将有助于其清晰性和可读性?
确保

5
@Secure:3dspline,4seasonPizza,2pdfConverter,8bitInt,...
用户未知

6
Forth允许。2DUP,2DROP,2SWAP,2> R,2R @,2R> 0 =,等:在内建的
彼得Mortensen的

一样TCL,但我不认为任何标准的TCL命令以数字开头
JK。

Answers:


51

在C / C ++中,数字后跟字母被认为是数字常量,其后的字符串限定了常量的类型。例如(这些是VC ++,不确定它们的标准):

  • 0-有符号整数
  • 0l-有符号长整数
  • 0u-无符号整数
  • 0i64-64位有符号整数

因此,a)对于Danieler来说,词法分析器比较容易,但b)由于0y 可能是变量,而0u 却不是,因此做出了明确的区分。再加上其他限定词,例如“ i64”,要比“ l”或“ u”晚添加,并且他们希望保持选择,如果需要的话可以添加更多。


7
同样,十六进制数字以0xd +的形式写入,其中d +是1个以上的十六进制数字0-f,因此0xbeef是一个完全有效的“数字”。
tcrosley 2012年

20
你们都知道我不是要制定语言规范,而是只提供了几个例子来说明要点,对吗?
DXM'2

6
回复:“他们希望保持在需要时添加更多内容的选项”:C ++ 11甚至允许您添加自己的内容;参见http://en.wikipedia.org/wiki/C++11#User-defined_literals
ruakh

2
我认为这不是正确的解释。“标识符不能以数字开头”规则适用于Algol,Pascal和其他不允许字母后缀为数字常量的语言。
拉里·格里茨

1
@LarryGritz:“用空格分隔单词一直是大约公元10世纪的普遍习惯,一直持续到1957年,当时FORTRAN放弃了这种做法。” —《 Sun FORTRAN参考手册》(来自Wiki)。Fortran有其自己的特殊原因,因为他们认为一般来说空格是可选的。现代语言喜欢它们的空白。Algol是您自己的,但我也不是现代人。另一方面,C / C ++ / C#/ F#都有后缀。
DXM 2012年

49

实施词法分析器的人员的便利。(不,很重要,仅此而已。各种语言都有其他原因,但最终归结为这一点。)


2
使用PEG或其他现代的解析技术,可以很容易地区分整数和以数字开头的标识符。甚至使用原始词法分析器的编译器也可以将它们放在相同的令牌类别中,并在以后进行区分。如果例如0flu是文字并且0glu是本地标识符,那将是非常尴尬的。
Daniel Lubarov

2
人们绝对有可能区分它们。该决定是基于便利性(或者,如果您不那么慈善,则是懒惰)而不是技术要求来做出的。
丹尼尔·皮特曼

2
@DanielPittman:您需要进行语义分析才能进行任何可靠的消歧,因此在词法分析器中无法做到。将决策推出词法分析器会使解析器更加复杂,并有什么好处?除了非常差的成本/收益情况之外,没有什么好办法来处理类似这样的情况。int 0u = 5; unsigned int x = 0u;但是,您选择定义此代码的解释(可能x == 0或x == 5),人们会感到困惑因为模棱两可。即使以这种方式实现编译器很简单,一个好的设计人员也可能不会这样做。
Joren 2012年

10
主要的便利在于我脑海中的解析器,而不是语言的创建者。
CodesInChaos 2012年

2
得知词法分析通常是编译器/解释器最慢阶段的一个很大因素,这仍然令许多人感到惊讶。
hippietrail 2012年

20

考虑以下两种情况:

情况1

假设标识符可以以数字开头。

因此,如下所示的语句将是有效的(因为标识符可以具有1个或多个字符):

int 3;

当我尝试在程序中使用以上变量时,将导致编译器模糊:

int 3,a;
3 = 5;
a = 3;

在语句a=3中3的作用是什么(它是一个值为5的变量还是数字3)?

情况二

与上面的示例相反,让我们假设一种语言实际上是允许以数字开头的标识符,而仍然不允许使用数字作为标识符。这可能会导致以下问题:

  • 有关变量的语言规则(该变量表示一个变量可以包含1个或多个字符)必须重新定义为复杂的规则,例如:变量可以具有一个或多个字符,并且如果不以数字开头则必须是唯一的。以数字(等)开头时,不能为单个字符长度。

  • 当所有数字(例如333)和有效字母后缀(例如34L)用作变量名时,编译器将必须检查并报告错误情况。在像Python和JS这样的松散类型语言中,您可以在不声明变量的情况下即时使用变量,甚至可能无法检查涉及所有数字的特殊情况,例如,if (33==5)此处33可能是用户声明的错误的未声明变量。但是编译器将无法识别此错误并报告错误。

进行此限制将防止程序员将数字用作标识符名称。


2
在这种逻辑下,标识符不能包含字符,因为它们与关键字不明确。您能想象int char = float会带来多大的灾难吗?
Pubby 2012年

4
@Pubby:我不知道您怎么能将我所说的内容完全推导出来,我还无法弄清楚。您的评论是什么意思?
aml90 '02

我是说您从字面上理解了这个问题,并且通过使用词法化优先级根本不是模棱两可。例如,编译器如何知道int是关键字而不是标识符?嗯,int就像数字词素一样,它具有更高的优先级。
Pubby 2012年

@Pubby:由于含糊不清,我的意思是编译器将不知道我在什么情况下使用变量名(即使使用词法优先级)。例如,考虑以下代码: int 3,a; 3=5; a=3; 在语句a = 3中,3是解释为标识符还是数字?这引起歧义。希望清楚。
aml90 '02

2
我也觉得这个论点很弱。编写一个词法分析器来接受以数字开头但不完全由数字组成的标识符将是微不足道的。
拉里·格里茨

11

在大多数情况下,这与简化编译器编写程序和简化分析效率无关,而与设计鼓励清晰易读的代码的语法有关。

语言设计人员认为,能够将数字1这样的数字文字写成纯1会很好。

这将是完全可能的设计,其中数字文字引述了以某种方式例如tildas所以对于一把手的数字小文字被编码为语言的语法〜1〜 ,任何事情不是关键字,而不是用引号括起来被视为一个变量名。

因此,您可以对如下语句进行编码:

1 = ~2~
two = 1 * ~2~

但是也:

2 = ~3~
six = 2 + 2

无论选择哪种语法,模棱两可且难以遵循的代码都是不可避免的。

C语言和C语言派生的大多数“花括号”语言也认为,允​​许程序员直接编写八进制和十六进制文字,并在重要的情况下指定文字的类型是一个好主意。所以

010  // Octal 10 = 8;
0x10 // Hexadecimal 10 = 16;
5l   // long integer with decimal value 5
2.0d // double float with value 2

因此,即使您允许变量名以数字开头,然后是数字和字母的组合(包括至少一个字母),也会给程序员带来问题,即确定给定的组是形成变量名还是数字文字,这样

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位机器值。因此,他们实际上需要八进制。

同样对于位旋转(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

第一个是do循环-迭代一个代码块10次。但是第二种方法是将逗号更改为小数点,因此它将值分配给1.10名为的变量do 10 i

这也意味着编写一个Fortran解析器相对困难-您无法确定do该行的开头确实是一个关键字,直到您到达该行的末尾,并验证了a的所有其他元素do循环存在。解析器通常必须做好“回溯”的准备,从一开始就重新解析行,以得到真正存在的“正确”(但通常是意外的)答案。

经过几年的努力,语言设计人员(无论如何还是大多数人)都走到了相反的极端-尽可能限制语言的几乎所有内容,而用户不会抱怨太多

例如,早期的BASIC基本上说您甚至不能使用关键字作为标识符的一部分 -例如,fora=1将其解析为for a = 1(即,for循环的开始,而不是赋值)。这显然引起了足够的抱怨,持续了很长时间。关于以数字开头的标识符的规则显然并没有引起很多抱怨,因此继续使用它(至少在大多数语言中)。


恕我直言,这是最接近真实原因的。在某些方面,诸如Fortran之类的早期语言过于结构化,从而导致编写健壮的编译器很困难,也给人类带来了以视觉方式正确解析源代码的困难。“ do10i = ...”是一个经典而著名的示例。随着语言的发展,一些规则变得更加严格。Algol可能是标准“标识符以字母开头,然后可以有字母或数字”的经验之父。
拉里·格里茨

FYI是Microsoft BASIC解释器,它是BASIC最受欢迎的微型计算机版本(包括Applesoft Basic和Commodore Basic)的基础,它使用贪婪的令牌生成器将与语言令牌匹配的任何字符序列转换为高位设置的字节值。无需任何语法分析即可完成此操作。然后,在运行程序时,解释器将假定找到的任何字母构成变量名称的一部分。
超级猫

1

这项约定可能是从很早的历史语言设计决策演变而来的,因为在早期的计算机上,整个编译器(包括词法分析)都必须运行几kWords,甚至比当前移动设备上的第一级处理器数据缓存还少的内存,因此,允许的变量名称非常有限,并且必须易于在很少的操作码中与数字常量区分开。

因此,这种约定成为了几代程序员习惯的习惯。


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 ..以便向编译器明确表示即将到来的令牌恰好是标识符或数字...存在此限制...此限制的原因...编译器仅通过扫描即将到来的令牌的第一个字符即可知道是标识符或数字。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.