为什么每一个都有冒号而不是“中”?


9

从Java 5语言指南

当您看到冒号(:)时,将其读为“ in”。

那么为什么不in首先使用呢?

多年来一直困扰着我。因为它与其他语言不一致。例如,在Java有implementsextendssuper对于类型,而不是符号之间的关系类似于C ++,Scala的或Ruby。

在Java中,冒号在5个上下文中使用。其中三个继承自C。另外两个则由Joshua Bloch认可。至少,那是他在“关闭争议”演讲中说的。当他批评冒号的映射与for-each语义不一致时,就会出现这种情况。在我看来,这很奇怪,因为这是每个人滥用的预期模式。喜欢list_name/category: elementslaberl/term: meaning

我已经窥探了jcp和jsr,但是没有发现邮件列表的迹象。谷歌未对此事进行任何讨论。只有新手对冒号的含义感到困惑for


in迄今为止提供的主要反对意见:

  • 需要新的关键字;和
  • 使词汇复杂化。

让我们看一下相关的语法定义:

声明
    :'for''(('forControl')'语句
    | ...
    ;

forControl
    :EnhancedForControl
    | forInit?';' 表达?';' forUpdate?
    ;

EnhancedForControl
    :variableModifier *类型variableDeclaratorId':'表达式
    ;

从更改:in不会带来额外的复杂性或需要新的关键字。


1
找出语言设计师动机的最佳来源通常是设计师自己。就是说,这显然只是一个语法糖,而不是一个可迭代的糖。看到stackoverflow.com/questions/11216994/...
罗伯特·哈维

Answers:


8

通常讲授的普通解析器在解析器接触输入之前有一个词法分析器阶段。词法分析器(也称为“扫描器”或“令牌生成器”)将输入切成带有类型注释的小令牌。这使主解析器可以将令牌用作终端元素,而不必将每个字符都视为终端,这可以显着提高效率。特别是,词法分析器还可以删除所有注释和空白。但是,单独的令牌生成器阶段意味着关键字也不能用作标识符(除非该语言支持某种程度上不受欢迎的straping或在所有标识符之前加上sigil前缀$foo)。

为什么?假设我们有一个简单的令牌生成器,它可以理解以下令牌:

FOR = 'for'
LPAREN = '('
RPAREN = ')'
IN = 'in'
IDENT = /\w+/
COLON = ':'
SEMICOLON = ';'

令牌生成器将始终匹配最长的令牌,并且优先使用关键字而不是标识符。因此interesting将被词汇化为IDENT:interesting,但in将被词汇化为IN,永不如此IDENT:interesting。像这样的代码片段

for(var in expression)

将被转换为令牌流

FOR LPAREN IDENT:var IN IDENT:expression RPAREN

到目前为止,这可行。但是任何变量in都将被分类为关键字IN而不是变量,这会破坏代码。词法分析器在标记之间不保留任何状态,并且in除非我们处于for循环中,否则不知道它通常应该是变量。另外,以下代码应合法:

for(in in expression)

第一个in是标识符,第二个是关键字。

对于此问题有两个反应:

上下文关键字令人困惑,让我们重新使用关键字。

Java有许多保留字,其中一些无用,只是向从C ++转换为Java的程序员提供了更有用的错误消息。添加新关键字会破坏代码。除非上下文上下文关键字具有良好的语法突出显示功能,否则添加上下文关键字会使代码的读者感到困惑,并且使工具难以实施,因为它们必须使用更高级的解析技术(请参见下文)。

当我们想扩展语言时,唯一明智的方法是使用以前在该语言中不合法的符号。特别是,这些不能是标识符。使用foreach循环语法,Java重用了:具有新含义的现有关键字。借助lambda,Java添加了一个->关键字,该关键字以前在任何合法程序中都不会出现(-->仍将被分类为'--' '>'合法,并且->可能先前被分类为'-', '>',但是该序列将被解析器拒绝)。

上下文关键字简化了语言,让我们实现它们

词法编辑器无疑是有用的。但是,我们可以在解析器前串联运行它们,而不是在解析器前运行词法分析器。自下而上的解析器始终知道在任何给定位置可接受的令牌类型集。然后,解析器可以请求词法分析器在当前位置匹配这些类型中的任何一种。在for-each循环中,解析器将在·找到变量后位于(简化的)语法中所指示的位置:

for_loop = for_loop_cstyle | for_each_loop
for_loop_cstyle = 'for' '(' declaration · ';' expression ';' expression ')'
for_each_loop = 'for' '(' declaration · 'in' expression ')'

在该位置,合法令牌是SEMICOLONIN,但不是IDENT。关键字in将是完全明确的。

在此特定示例中,自上而下的解析器也不会出现问题,因为我们可以将上述语法重写为

for_loop = 'for' '(' declaration · for_loop_rest ')'
for_loop_rest =  · ';' expression ';' expression
for_loop_rest = · 'in' expression

并且无需回溯就可以看到决策所需的所有令牌。

考虑可用性

Java一直倾向于语义和句法简化。例如,该语言不支持运算符重载,因为这会使代码复杂得多。因此,在为for-each循环语法选择之间in以及:为每个循环使用语法时,我们必须考虑哪种混淆方式对用户更不明显。极端的情况可能是

for (in in in in())
for (in in : in())

(注意:Java为类型名称,变量和方法提供了单独的命名空间。我认为这主要是一个错误。这并不意味着以后的语言设计必须添加更多的错误。)

哪种选择可以在迭代变量和迭代集合之间提供更清晰的视觉分隔?扫一眼代码,可以更快地识别出哪种方法?我发现在涉及这些标准时,分隔符号要比字符串更好。其他语言具有不同的值。例如,Python用英语拼写了许多运算符,以便可以自然地阅读它们并易于理解,但是这些相同的属性可能使乍一看很难理解一段Python。


17

Java 5中添加了for-each循环语法。您必须创建in一个language关键字,以后要不惜一切代价避免将关键字添加到一种语言中,因为这会破坏现有代码-突然,所有命名的变量都会in 引起解析。错误。enum在这方面已经很糟糕了。


2
似乎不方便。前提是语言设计者足够优秀,可以从一开始就预测大多数必需的关键字。我不确定是否有必要;体面的编译器可以根据其上下文确定关键字是否为变量。
罗伯特·哈维

2
我不认为Java具有像C#一样的上下文关键字。因此,使用in将意味着引入一个新的关键字,从而破坏向后兼容性(System.in,是否有人?)或引入一个以前未知的全新概念(上下文关键字)。都是为了什么?
约尔格W¯¯米塔格

2
上下文关键字有什么危害?
user2418306 '16

5
@ user2418306添加关键字不必破坏现有代码,只要未使用单独的词法分析器阶段解析该语言即可。特别是,for(variable in expression)即使“ in”可以用于变量,“ in”输入也绝不能与任何法律代码相歧义。但是,在许多编译器工具链中,单独的词法分析器阶段非常普遍。这将使使用某些常见的解析器生成器解析Java成为不可能,或者至少要困难得多。简化语言的语法通常对所有相关人员都有好处。并非每个人都需要像C ++或Perl这样的语法怪异的东西。
阿蒙

1
@RobertHarvey:别忘了constgoto它们都是Java中的保留字,但尚未使用(至今)。
TMN
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.