当与“ if”和“ while”一起使用时,为什么语言需要在表达式周围加上括号?


67

如C,Java中,和C ++语言在使用时都需要围绕整个表达式括号ifwhileswitch

if (true) {
    // Do something
}

相对于

if true {
    // Do something
}

我觉得这很奇怪,因为括号是多余的。在此示例中,true是一个单独的表达式。括号不会以我所知的任何方式改变其含义。为什么存在这种奇怪的语法?为什么如此常见?我不知道有什么好处吗?


20
Pascal不需要括号(因为它需要一个THEN)。
JimmyB,2016年

30
Python,Ruby没有。
smci

31
我相信C使用括号,因为括号对于单语句主体是可选的。或许更好的说法是,大括号不是if语句的一部分,它们只是创建了复合语句。
Fred Larson

7
有趣的是,Go需要大括号而不是括号。
科斯

25
问题有点重言。为什么圆形人孔盖全面?为什么所有的兄弟都是男性?为什么需要paren的语言都需要paren?根据定义,圆形井盖是圆形的;兄弟是男性。需要parens的语言按定义需要parens。
埃里克·利珀特

Answers:


155

需要某种方式来告知条件在何处结束以及分支开始于何处。有许多不同的方法可以做到这一点。

在一些语言中,有没有条件语句可言,如在Smalltalk,自考,新话,IO,伊欧凯,SEPH和花式。与其他方法一样,条件分支只是作为常规方法简单实现的。该方法在布尔对象上实现,并在布尔上被调用。这样,条件就是方法的接收者,并且两个分支是两个参数,例如在Smalltalk中:

aBooleanExpression ifTrue: [23] ifFalse: [42].

如果您更熟悉Java,则等效于以下内容:

aBooleanExpression.ifThenElse(() -> 23, () -> 42);

在Lisp语言家族中,情况类似:条件只是普通函数(实际上是宏),第一个参数是条件,第二个和第三个参数是分支,因此它们只是普通函数参数,并且不需要特别的分隔它们:

(if aBooleanExpression 23 42)

某些语言使用关键字作为分隔符,例如Algol,Ada,BASIC,Pascal,Modula-2,Oberon,Oberon-2,Active Oberon,Component Pascal,Zonnon,Modula-3:

IF aBooleanExpression THEN RETURN 23 ELSE RETURN 42;

在Ruby中,您可以使用关键字或表达式分隔符(分号或换行符):

if a_boolean_expression then 23 else 42 end

if a_boolean_expression; 23 else 42 end

# non-idiomatic, the minimum amount of whitespace required syntactically
if a_boolean_expression
23 else 42 end

# idiomatic, although only the first newline is required syntactically
if a_boolean_expression
  23
else
  42
end

Go要求分支必须是块,并且不允许使用表达式或语句,这使花括号成为必需。因此,虽然您可以根据需要添加括号,但不需要括号。Perl6和Rust在这方面是相似的:

if aBooleanExpression { return 23 } else { return 42 }

某些语言使用其他非字母数字字符来分隔条件,例如Python:

if aBooleanExpression: return 23
else: return 42

最重要的是:您需要某种方法来告知条件在何处结束以及分支开始于何处。这样做的方法很多,括号只是其中之一。


8
很好的概述。
彼得·施耐德

2
当然,在某种语言中,要么裸表达式不是语句(例如,较早的BASIC之类的东西,即必须将计算值分配给变量或传递给其他语句),要么没有前缀运算符,您应该始终能够标识表达式的结尾和语句的开头。我绝对可以在IF语句的末尾看到没有分隔符的BASIC变体管理。
Periata Breatta

4
同样,由于C是在70年代设计的,并且计算量很大,因此添加一点括号可能会使解析器的编写更加容易。
马查多

4
回复:Lisp:“实际上是宏”。实际上,IF是Scheme和CL中的一种特殊形式(仅出于完整性考虑)。
coredump

1
@Leushenko:例如,在默认情况下是惰性的MISC中,所有条件形式都只是普通函数,既不是宏也不是特殊形式。(的确,AFAIR,MISC 有零种特殊形式吗?)
Mittag

70

仅在使用大括号时才需要括号。

if true ++ x;

例如,如果没有它们,就会变得模棱两可。


28
@RobertHarvey- 我知道的几乎每种语言需要括号。当然是C及其近亲。该OP是问为什么他们需要-这是因为语言会变得模糊,否则。
Telastyn '16

25
就在我的头顶上,不需要括号if:基本,汇编,python,bash / zsh,tcl,批处理,brainfuck或机器代码。仅if当语言被设计为依赖括号时,才缺少括号。
candied_orange

12
令我惊讶的是,没有人提到最合乎逻辑且可读性最高的版本-在Pascal(包括Delphi)中if Condition then ...
Ulrich Gerhardt's

18
Go是一个相反的好例子。它使花括号为{}强制性的,因此不需要在表达式周围加括号。不仅不需要parens,而且如果我记得正确添加parens会导致编译错误-禁止使用它们
slebetman

10
@Eiko让我改一下。答案中的示例在语法上是模棱两可的,即使它在语义上是模棱两可的(如您所指出的)。但是,由于解析阶段发生在语义分析之前,因此解析器将遇到歧义-并且它必须做出无根据的猜测或失败。如果(出于任何原因)解析器选择不失败,那么语义分析器将与结果树一起使用,无论结果如何。我还没有见过一个编译器,其中语义分析器愿意让解析器重新解析子树,并在语法上模棱两可的构造上做出不同的选择。
Theodoros Chatzigiannakis's

21

在括号中if的语句没有意义的算术表达式中使用括号一样。算术表达式中的括号用于将表达式分组在一起。if语句中的括号用于分隔布尔表达式;也就是说,将布尔表达式与if语句的其余部分区分开。

if语句中,括号不执行分组功能(尽管在if语句内,您仍可以使用括号对算术表达式进行分组。然后,括号的外部集用于界定整个布尔表达式)。将它们设为必需可以简化编译器,因为编译器可以依赖始终存在于其中的那些括号。


我看不出它如何简化编译器。规则'IF'('expression')'statement`比 IF primary_expression statement。注意,后者同样是明确的。
user58697 '16

@ user58697:不,只有后者具有模棱两可的含义,即primary_expression不能将表达式声明中的后缀运算符与前缀运算符区分开。要复制Telastyn的答案,if true ++ x;。同样,如果存在空语句,if a & f;则可以是&条件内部的空语句和二进制,也可以是语句&开头的一元。但是,当括号匹配时,开头恰好有一个匹配项(
MSalters

@MSalters后缀运算符并不能解析作为主。一个主要表达是之一IDENTIFIERCONSTANTSTRING_LITERAL'(' expression ')'
user58697 '16

@ user58697:您似乎在记住一种特定的语言。并且似乎有一个规则,即且仅当条件为“ IDENTIFIER,CONSTANT或STRING_LITERAL”时才不需要括号。我不确定这会使事情变得容易。
MSalters

16

正如其他人已经部分指出的那样,这是由于表达式也是有效的语句,并且在只有一个语句的块的情况下,您可以删除花括号。这意味着以下内容不明确:

if true
    +x;

因为它可以解释为:

if (true + x) {}

代替:

if (true) {+x;}

许多语言(例如Python)使您可以避免使用括号,但仍然具有结束条件标记:

如果为True  + x

但是,您可以定义一个永远不需要括号的语言是正确的:表达式不是有效语句的语言将不会出现此问题。

不幸的是,这意味着:

 ++x;
 functionCall(1,2,3);

不是有效的语句,因此您必须引入一些怪异的语法才能执行此类操作而无需创建表达式。一种简单的方法是在表达式前面加上一个标记,例如[statement]

[statement] ++x;
[statement] functionCall(1,2,3);

现在,由于您必须编写,歧义消失了:

if true
    [statement] ++x;

但是如您所见,我看不到这种语言的广泛使用,因为将括号括在if-condition(或:末尾)周围要比为每个表达式语句放置这样的标记要好得多。


注意:使用[statement]标记只是我能想到的最简单的语法。但是,对于表达式和语句,您可能有两种完全不同的语法,它们之间没有歧义,不需要这样的标记。问题是:由于要在表达式或语句中执行相同的操作,因此您必须使用完全不同的语法,因此该语言将非常奇怪。

想到的是,有两种没有这种显式标记的单独语法,例如:强制语句使用unicode符号(因此,不是for使用字母fo和的unicode变体r,而表达式是仅ASCII。


2
实际上存在一种使用这种表达式语句标记的语言:如果要评估表达式的副作用,则需要discard在Nim中显式地指定其值。但是,这样做只是为了类型安全,而不是出于语法原因。
阿蒙

@amon尼斯,我对此一无所知。无论如何,正如我说过的那样,实际上并不需要标记,这只是在不发明直观语法的情况下实现这种区分的一种简单方法。
巴库里

1
@amon-许多BASIC变体在表达式和语句之间也有严格的分隔。仅在实际使用该值的地方才允许使用表达式(例如,变量赋值,执行动作的语句如PRINT等)。不用于计算值的过程由关键字(通常我知道的至少一个变体使用“ PROC”)调用,该关键字使用它们的名称作为前缀。等等。BASIC通常在IF语句中用“ THEN”定界表达式的末尾,但是我看不出没有任何技术理由不能放弃该要求。
Periata Breatta

1
在80年代,有一种非常流行的语言,它的块没有括号,花括号,冒号或其他标记,并且可以在任何地方接受表达式作为语句,并且某些语句的行为类似于表达式(复合运算符,例如+ =和++)。糟糕的是,编译器之前有一个笨拙的预处理程序(?例如,simbol实际上是PP之后的函数)。没有;。当然,它需要一个用于续行的标记,但是不建议这样做。harbour.github.io/doc/clc53.html#if-cmd。该编译器既快速又简单(使用Bison / Flex创建)。
Maniero

@bigown他们实现,通过使用一个单独的语法的逻辑条件,因此,基本上,对于条件ifwhileECC被限制相比,在其他语言中使用的通用的表达式。当然:如果您有两个以上的语法类别(例如陈述,表达,逻辑表达,咖啡制作表达等),则可以交易一些自由。
Bakuriu

10

C系列语言通常要求使用这些括号,但并不通用。

一个Perl 6的的更明显的语法变化是,他们修改了语法,这样你就没有给周围的括号iffor和类似的声明的条件。因此,类似这样的事情在Perl 6中是完全有效的:

if $x == 4 {
    ...
}

照原样

while $queue.pop {
    ...
}

但是,由于它们只是表达式,您可以根据需要在其周围加上括号,在这种情况下,它们只是普通的分组形式,而不是C,C#,Java等语言中语法的必需部分。

Rust在该部门的语法与Perl 6类似:

if x == 4 {
    ...
}

在我看来,受现代C语言启发的语言的一个特征是查看此类内容并想删除它们。


虽然您的答案提供了对其他语言的一些了解,但并未回答“为什么存在这种奇怪的语法,为什么它如此普遍?”

你说得很对。我真的想在问题中添加上下文,在这个平台上这并非易事。我猜想,如果我确实回答了这个问题,那就是“仅仅是因为,因为没有技术上的理由,语法不能容纳没有语法的人”。但这不是一个非常有用的答案。
马修沃尔顿

在Perl 5中,两者都有。对于带有BLOCK 的常规 if或循环构造,需要使用paren,例如in if ( $x == 4 ) { ... }foreach my $foo ( @bar ) { ... }。使用后缀表示法时,括号是可选的,如return unless $foo;或中所示++$x while s/foo/bar/g;
simbabque

6

有一个方面令我感到惊讶,现有的答案都没有提出。

C以及许多C派生和相似项具有一个特点,即赋值是赋值。这样的结果是可以在期望值的地方使用分配。

这使您可以编写类似

if (x = getValue() == 42) { ... }

要么

if (x == y = 47) { ... }

要么

unsigned int n = 0 /* given m == SOME_VALUE */;
while (n < m && *p1++ = *p2++) { n++; }

(这被隐式地视为while (n < m && *p1++ = *p2++ != 0) { n++; }因为C将非零视为true;顺便说一句,我认为这与C标准库中的strncpy()差不多)

甚至

if (x = 17);

这都是有效的。并非所有语法有效的组合都一定有用(现代编译器特别警告条件语句中的赋值,因为它是一个常见错误),但其中某些实际上有用。

如果没有明确的方法来确定条件表达式的开始和结束位置,则解析此类语句可能会更加困难。

括号已用于从函数参数中分隔函数名称,因此我想它们似乎也很自然地从关键字参数中分隔关键字。

当然,可以定义其他语法来执行相同的操作。但是这样做会增加复杂性,尤其是在解析器中,然后在很大程度上需要处理两组不同的语法。早在设计C时,计算能力(无论是数字运算能力,工作内存还是存储容量)都极为有限。几乎不用付出任何代价就可以降低复杂性的任何事情,几乎肯定是可喜的变化。

在今天,使用括号似乎有些陈旧,但与拥有某种语言的人并不熟悉的情况相比,与能够表达相同内容的其他语法相比,这会损害可读性。


5

原因主要是历史。

在编写第一个C编译器时,计算机的ram,cpu和编译器非常有限,它们是用“手工”编写的,几乎没有什么工具可以帮助编译器编写者。因此,复杂的规则在编译器中实现成本很高。C ++,C#,Java等都是为了使C程序员易于学习而设计的,因此没有进行“不必要的”更改。

在“ c like”语言中,条件语句(if, while, etc)不需要显式的blockoff代码,您只需使用简单的语句即可。

if (a == d) doIt()

或者您也可以结合在一起的语句为compound statement通过将它们与{}

我们希望编译器找到我们所犯的错误,并给出我们可以理解的错误消息。


3

Java和C ++都是在C成为非常流行的编程语言之后开发的。在设计每种语言时,要考虑的一个因素是它将吸引C程序员并吸引这些程序员使用新语言。(我曾经是他们成功吸引的C程序员之一。)C ++的设计目的是(几乎)可以与C代码互换。为了支持这些目标,无论是C ++和Java采用了很多C'S语法,包括周围的条件括号ifwhileswitch语句。

因此,所有这些语言在这些语句的条件周围都需要括号的原因是因为C确实需要这样做,而问题实际上就是C为什么需要这些括号。

本文的主要作者之一丹尼斯·里奇(Dennis Ritchie)本文中描述了C语言的起源 (有人甚至可以说是其开发主要作者)。如该文章所述,C最初是在1970年代初开发的,它是用于主存空间非常有限的计算机的系统编程语言。希望有一种比汇编语言更高级的语言,但是鉴于可用的资源,解析该语言的简便性也很重要。要求括号将使识别条件代码相对容易。

也许还可以推断出使用较少字符来编写程序的能力被认为是一种优势,并且两个括号所占用的空间比THEN当时FORTRAN和其他高级语言所使用的关键字少;实际上,由于括号也可以代替空格作为符号的分隔符,if(a==b)因此比短了四个完整字符IF a==b THEN

无论如何,在人类阅读,编写和理解用C编写的程序的容易程度,编译器解析和编译用C编写的程序的难易程度以及多少千字节(!)之间必须取得一定的平衡。程序源和编译器本身都将需要它。和周围的条件括号ifwhileswitch 语句是人们如何选择罢工C的设计平衡

正如其他几个答案所证明的那样,一旦您摆脱了C语言开发的特殊环境,就已经将各种其他形式的语法用于各种编程语言的条件。因此,括号实际上只是由少数人在历史上的某个特定时间在一定约束下做出的设计决定而做出的。


我不确定是否可以说C ++是“为了吸引那些程序员使用新语言”而设计的。还记得C和类吗?
CVn

@MichaelKjörling诚然,Java开发人员对“求爱”更为明确。但是请注意,链接的文章引用了Stroustrup选择以C开头作为其语言基础的原因之一,因为C 被广泛使用。提供接近C语言的动机的一种方法是,因为可以轻松地对现有代码进行适应(如我已经说过的),但是现有的编码人员也可以轻松地适应。
David K

@MichaelKjörling我想答案的原始措词表明,“求爱”是语言设计中比实际更大的因素。我已经编辑了答案,以试图阐明这只是语言设计中的件事。
David K

3

这里有许多理由认为,如果没有括号,该语法将是模棱两可的,并且无声地暗示这将是一种糟糕的情况,甚至是不可能的情况。

实际上,语言有很多方法可以处理歧义。运算符优先级只是该主题的一个实例。

不,模棱两可不是括号的原因。我想一个人可以简单地创建一个C版本,该C版本不需要条件周围的括号(因此使它们成为可选的),并且在所有情况下仍可以创建有效的代码。的示例if a ++ b;可以解释为等同于if (a) ++b;if (a++) b;,无论哪种看起来更合适。

为什么丹尼斯·里奇(Dennis Ritchie)选择使()为强制性(并因此为许多派生语言创造了这种模因)的问题是一种语言学的问题。我猜想这样的想法清楚地表明,条件是一种表达,而不是命令是思想之源。

实际上,C被设计为使用一遍解析器可解析的。在条件周围使用带有强制括号的语法可支持此方面。


我对我的回答不满意。请善于在评论中解释您不满意的地方。也许我可以改善它。
Alfe

0

if在Fortran,Cobol,PL / 1,Algol,Algo-68,Pascal,Modula,XPL,PL / M,MPL等任何带then关键字的语言中,不需要在条件周围加括号。then用于condition从以下内容中划定界限statement

C等中的右括号用作then,而右括号在形式上是多余的。

以上说明适用于传统解析的语言。


Fortran 确实在其所有版本的IF(包括结构性版本)中都需要使用括号。
Netch
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.