为什么分号和逗号在for循环中互换?


49

在许多语言中(从C到JavaScript的广泛列表):

  • 逗号,分隔参数(例如func(a, b, c)),而
  • 分号;分隔顺序指令(例如instruction1; instruction2; instruction3)。

那么,为什么要使用相同的for循环语言来反转此映射:

for ( init1, init2; condition; inc1, inc2 )
{
    instruction1;
    instruction2;
}

而不是(对我来说似乎更自然)

for ( init1; init2, condition, inc1; inc2 )
{
    instruction1;
    instruction2;
}

当然,for(通常)不是一个函数,但是参数(即initconditionincrement)的行为更像指令序列的函数的参数。

是由于历史原因/惯例造成的,还是in ,;in循环互换的合理依据?


1
(我在这里的第一篇文章。我不确定这个问题是否更多属于Programmers或SO,因此,如有需要,可以随时进行迁移。)
Piotr Migdal

8
绝对是程序员的帖子。欢迎!:-)
Martijn Pieters

1
“为什么不”将提供答案-通过具有相同的答案-“因为某人需要做出选择,那就是他们做出的选择”与“他们为什么选择“ {”和“}”以及其他1000个选择相同他们做了-“因为”
马特兹

2
@mattnz问题是关于一致性(不是“为什么我们;不使用|?”(或为什么我们使用“ else”而不是“ otherwise”?)),这不是一种语言的情况,而是大量的语言。一个很好的答案是完全可以的,例如“它是用C语言编写的while循环的缩写(并且稍后才考虑使用inc的多个语句),而人们不想更改它以免引起程序员的兴趣”。
Piotr Migdal

我回想起-也许在K&R中读过-逗号运算符最初是添加到该语言中的,以便可以在for语句的init表达式中初始化多个变量。
zwol13年

Answers:


18

那么为什么用相同的语言将这样的映射反转为for循环。

从技术上讲,映射不是“反向的”。

  • 用逗号分隔的内容不是参数。在(至少)C ++和Java中,它们可以是声明,因此它们甚至不是表达式。
  • 用分号分隔的内容也不是(单个)语句。

实际上,我们在这里使用的是不同的语法上下文,其中相同的符号被不同地使用。我们没有像“喜欢”那样进行比较,因此没有映射,也没有针对基于语义一致性的一致映射的有力论据。

那么为什么不这样做呢?

嗯,我认为原因来自于“自然”的含义,;。在英语书面语言中,分号比逗号更“强”,而分号的标志符号比逗号更明显。这两件事结合起来使当前的安排(在我看来!)显得更加自然。

但是唯一可以确定为什么选择语法的方法是C设计师能否告诉我们他们在1970年之前的想法。我怀疑他们对过去做出的技术决定是否有清晰的记忆。


是由于历史原因/惯例

在C之前,我不知道有任何语言将类似C的语法用于“ for”循环:

  • Donal Fellows指出BCPL和B没有等效的构造。

  • FORTRAN,COBOL和Algol-60(和Pascal)的等效项表达较少,并且语法与C的“ for”语法不同。

但是C之后的C,C ++和Java之类的语言显然都借用了C的“ for”语法。


因此,(,vs ;)的原理是(较弱vs较强的突破),而不是(tuple- vs序列分割符),对吗?对我来说,参数或语句是否需要更强的中断(在许多情况下,对于一系列语句而言,中断是隐式的(例如,参见JavaScript(例如i++[line break]j++)))仍然不明显,但是至少现在我明白了当前约定的原因没有“明显逆转”。
Piotr Migdal

@PiotrMigdal用作分隔符的逗号将阻止使用逗号操作数,并且可能暗示for循环的组件是语句而不是表达式。这具有重要的意义。

最后的评论让我很好奇BCPL所做的事情,但是很显然,它是FOR i = e1 TO e2 BY e3 DO c(e1..e3表达式,c命令),它更类似于BASIC的语法。来源
CVn 2013年

1
@PiotrMigdal-“哲学”就是K&R和其他人在1970年回想的内容。我认为这并不像您想象的那样深入。(他们试图实现一种“高级”语言,以避免不得不在汇编器中编写大量的电话交换软件。)
Stephen C

我刚刚检查过;该for语法是C(它不是在引入或BCPL)。
Donal Fellows

60

我们编写如下循环:

 for(x = 0; x < 10; x++)

可以定义语言,使循环看起来像:

 for(x = 0, x < 10, x++)

但是,请考虑使用while循环实现的相同循环:

 x = 0;
 while(x < 10)
 {
     x++;
 }

注意,x=0and x++是语句,以分号结尾。它们不是函数调用中的表达式。分号用于分隔语句,并且由于for循环中的三个元素中的两个是语句,因此就使用了分号。for循环只是while循环的快捷方式。

此外,参数实际上并不像函数的参数那样起作用。重复评估第二和第三。的确它们不是序列,但它们也不是函数参数。

同样,可以在for循环中使用逗号来包含多个语句这一事实实际上是可以在for循环之外执行的操作。

x = 0, y= 3;

即使在for循环之外也是一个非常有效的语句。我不知道在for循环之外有任何实际用途。但是重点是逗号总是细分语句。这不是for循环的特殊功能。


当然,我知道“ while”循环是“更基本的”。但是这种“简写形式”没有什么意义(至少对我而言),因为您可以从x = 0; y = 0;和开始(在大括号内)x++; y++;...
Piotr Migdal 2013年

2
@PiotrMigdal,哦,可以。我的观点是,for循环内的部分是语句(由分号分隔)而不是表达式(由逗号分隔)
Winston Ewert

1
我得到的区别是,对我来说,;很自然地需要一系列语句,而不必分隔任何语句(所以只是口味不同吗?)。在目前的惯例中,无论如何有时还是会以逗号分隔语句的序列 ...
Piotr Migdal 2013年

3
@PiotrMigdal,结构/联合的成员由分号分隔,但实际上并不是顺序的。因此,它的使用肯定不限于语句序列。归根结底,语法确实让人难以接受。
Winston Ewert

我不知道在for循环之外有任何实际用途---怎么样(foo)?bar++, qux++:bletch---您希望?:表达式在其中做两件事而不只是一件事情。如果返回值foo是真实的qux,但两者barqux得到增加。

15

在C和C ++中,这是逗号运算符,而不仅仅是逗号。

for循环的语法类似于

for ([pre-expression]; [terminate-condition]; [increment-expression]) body-expression

对于您的问题:

pre-expression -> init1, init2
terminate-condition -> condition
increment-expression -> inc1, inc2

请注意,逗号运算符允许您在一条语句中执行多个操作(如编译器所见)。如果您的建议得到了实现,那么语法将在程序员打算编写逗号运算符或分隔符的时间上含糊不清。

简而言之,;表示语句结束。一个for循环是一个关键词,然后通过包围可选语句列表()。逗号运算符语句允许,在单个语句中使用。


3
for循环是由分号分隔的一组表达式。语句可以不仅仅是表达式-不能将case语句或if语句放到for循环部分中。当人们

@MichaelT:但是在C ++中,for循环的语法明确允许将语句(声明)作为其第一部分。(C ++允许声明中间功能,而不是其前身C89)。您无法跨语言来概括这样的语句,即使对于C和C ++之类的两种语言也是如此。
MSalters

@MichaelT您是否错过了“有点像”部分?
詹姆斯,

@詹姆斯可以避开“像”使用的实际BNF for ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>çfor ( for-init-statement; conditionopt ; expressionopt ) statement用于C ++ ---的“;” 不仅表示语句终止符。for循环后没有()中包含的语句。

8

没有概念上的逆转。

C中的分号代表的主要划分要多于逗号。它们将语句和声明分开。

for循环的主要区别是有三个表达式(或一个声明和两个表达式)和一个主体。

在C for循环中看到的逗号不是for循环语法的一部分。它们只是逗号运算符的体现。

逗号是函数调用中的参数之间以及函数声明中的参数之间的主要分隔符,但不使用分号。for循环是特殊语法;它与函数或函数调用无关。


2

也许这是C / C ++特有的,但我发布了这个答案,因为您所描述的滞后句法主要受C语法的影响。

从技术的角度来看,除了先前回答的问题是正确的之外,这还因为在C(和C ++)中,逗号实际上是一个运算符,甚至可以重载。使用分号运算符(operator;())可能会使编写编译器更加困难,因为分号是公理表达式终止符。

引起这种干扰的原因是,逗号在整个语言中被广泛用作分隔符。看起来逗号运算符是个例外,它主要用于获取for具有多个条件的-loops,因此该怎么办?

实际上,它operator,是为执行与定义,参数列表等相同的操作而构建的:它是为分隔表达式而构建的-语法构造,无法做到这一点。它只能区分标准中定义的内容。

但是,分号不会分开,它会终止。这也是导致我们回到原始问题的原因:

for (int a = 0, float b = 0.0f; a < 100 && b < 100.0f; a++, b += 1.0f)
    printf("%d: %f", a, b);

逗号在三个循环部分中分隔表达式,而分号终止循环定义的一部分(初始化,条件或事后思考)。

较新的编程语言(如C#)可能不允许重载逗号运算符,但是它们很可能保留了语法,因为更改它感觉有些不自然。


这个说法有问题。在for声明中,该;符号通常用作分隔符。它将语句的3个语法部分分开。没有第三个分号可以“终止”渐进式表达式列表。它以不同的令牌-终止)
Stephen C

0

对我来说,它们所使用的含义与其语言意义更相似。逗号用于列表和分号,其中包含更多单独的部分。

在其中func(a, b, c)有一个参数列表。

instruction1; instruction2; instruction3 可能是一个列表,但是是单独且独立的指令列表。

而在其中,for ( init1, init2; condition; inc1, inc2 )我们分为三个部分-初始化列表,条件列表和增量表达式列表。


0

看到它的最简单方法是:

for(x = 0; x < 10; x++)

是:

for(
x = 0;
x < 10;
x++
)

换句话说,x = 0的东西实际上是一条语句/指令,而不是参数。您在此处插入一条语句。因此,它们之间用分号分隔。

实际上,它们不可能用逗号分隔。上一次何时插入x <10这样的参数作为参数?如果要使计算机x <10一次,并将该操作的结果作为参数插入,则执行此操作。因此,在逗号世界中,如果要将x <0的值传递给函数,则可以放置x <10。

在这里,您指定每次传递循环时程序应检查x <10。这是一条指令。

x ++绝对是另一条指令。

这些都是说明。因此它们之间用半冒号隔开。


这不是声明。它是一个用分号分隔的表达式。声明完全不同。

x <10可能是一个表达式(通常用分号分隔
。x

看一下Cbnf-如果for循环是语句,则可以使用其他语句,例如另一个语句for switchreturn for循环定义(即for(int i = 0; if(i > 1024) { return; } ; switch (i % 3) { case 0; case 1: i++; case 2: i++; } ) { ... })内---不能。这不是声明。而是将其定义为for ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>

奇怪。int i = 0是一个好的表达式,但是我们主要是声明一个int即i并为其分配0(它也返回0,但有副作用。您不能这样做({int i = 0; j = i}; j <0; cout <<“ Hello world”)可以吗?还是,我认为你可以
user4951 2013年

1
@JimThio:您可能没有意识到,但是“声明”和“表达”在语言标准中具有非常精确的含义。int i = 0绝对不是表达。考虑到可以构成表达式的规则,描述表达式的规则集非常复杂,但是构造TYPE NAME = EXPRESSION与这些规则都不匹配。
MSalters
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.