即使在没有必要的情况下,我也应该在逻辑语句中使用括号吗?


100

假设我有一个布尔条件,a AND b OR c AND d并且我使用的语言中AND的操作先例比的先后顺序高OR。我可以编写以下代码行:

If (a AND b) OR (c AND d) Then ...

但实际上,这等效于:

If a AND b OR c AND d Then ...

是否有支持或反对包含多余括号的论点?实践经验是否表明值得将它们包括在内以提高可读性?还是表明开发人员需要真正坐下来并对他们的语言基础充满信心?


90
我可能很懒,但是为了便于阅读,在大多数情况下我都希望使用括号。
thorstenmüller2013年

6
我也是。我只是希望我为提高可读性而少做些事情,因为我懒得对我的语言基础变得自信/称职。
Jeff Bridgman 2013年

16
正确使用括号就像正确使用语法一样。2 * 3 + 2可能与第一个相同,(2 * 3) + 2但第二个更易于阅读。
Reactgular 2013年

16
@Mathew如果您数学不佳,也许可以。对于更复杂的情况,请确保使用括号。但是对于盲目的明显的对象(BODMAS…),由于杂乱,它们的可读性要比辅助它降低更多。
Konrad Rudolph 2013年

3
就是说,AND / OR在Basic,Python,SQL中具有相同的优先级……我的印象是,这是绝大多数现代语言(尽管不是全部)中的规则。
蒂姆·古德曼

Answers:


117

优秀的开发人员努力编写的代码是明确的正确的。即使没有严格要求,带条件的括号也可以帮助您。

为了清楚起见,请考虑一下括号,例如代码中的注释:它们并不是严格必须的,并且从理论上讲,有能力的开发人员应该能够在没有它们的情况下找出代码。但是,这些提示非常有用,因为:

  • 它们减少了理解代码所需的工作。
  • 他们提供了开发者意图的确认。

此外,额外的括号(如缩进,空格和其他样式标准)有助于以逻辑方式直观地组织代码。

至于正确性,没有括号的条件是愚蠢错误的诱因。当它们发生时,它们可能是很难发现的错误-因为通常情况下,错误的条件在大多数情况下都会正确执行,并且偶尔会失败。

即使您做对了,下一个处理您的代码的人也可能不会这样做,要么在表达式中添加错误,要么误解您的逻辑,从而在其他地方添加错误(正如LarsH正确指出的那样)。

我总是用括号相结合的表现形式andor(也用于算术运算类似的优先问题)。


3
虽然,我听说它说注释是道歉的(对于不好的/难读的代码)……您很有可能可以更好地编写它。我想您可以对括号说类似的话。
Jeff Bridgman

6
正确性的另一个方面是通过更改来保持正确性:尽管最初的开发人员在第一次编写代码时就牢记了优先权而没有括号,但他(或另一个)后来出现了,但他并没有记住全部内容。当它们在表达式中添加更多术语时,这些细节很可能将其弄乱。(这已经隐含了很多,但是我觉得值得强调。)
LarsH 2013年

1
@LarsH,谢谢,我将其明确添加到了答案中。

8
+1“他们提供对开发者意图的确认。” -任何程序员(可以,也许不是全部,但是所有驻留在这里的人....)都可以计算出编译器将使用最复杂的逻辑做什么。绝对没人能确定最初的开发者打算(包括自己在内)在几周内完成除最简单的事情之外的任何事情.....
mattnz 2013年

2
我认为@JeffBridgman引用了一个众所周知的观点,即“注释有时可能是代码的味道”。例如,请参阅Jeff Atwood的摘要,其中包括问题“您可以重构代码,而不需要注释吗?”。我认为如果您的评论解释了为什么您的代码如此不直观,那肯定可以暗示某些地方出了问题。在这样的时候,简化代码是个好主意。我完全同意您的实际回答,但是请加上括号。
Daniel B

94

它的事项是否少在你的语言的把握有信心。更重要的是紧随您之后的n00b语言的掌握。

以最清晰,最明确的方式编写代码。多余的括号通常(但不总是)有帮助。在一行上仅放置一个语句通常会有所帮助。编码风格的一致性通常会有所帮助。

括号太多了,但这是您不需要建议的情况之一-当您看到它时便会知道。到那时,重构您的代码以减少语句的复杂性,而不是删除括号。


72
即使您是生病,疲倦或处理去年编写的代码的单独开发人员,也不要忘记。您的理解水平将降至n00b。
Dan Neely 2013年

30
There is such a thing as too many parenthesis-你显然不是一个利斯佩尔;)
保罗

18
不好的建议:不要为菜鸟写东西。这将降低您的代码质量(相当可观),因为您不能使用超出初学者书籍前两章的公认习语。
Konrad Rudolph

10
@KonradRudolph:也许是这样,但是不要写那些只从头到尾地了解计算机编程艺术的人,或者不准备在以后调试其代码,并且不知道为什么要这样做的线索的人。阅读的代码远不止编写代码。
haylem 2013年

15
@dodgethesteamroller:我已经看到太多能干/受人尊敬的开发人员引入了优先级错误(每个人时不时都有糟糕的日子),这些错误多年来一直没有被注意到。对于确实了解优先规则的优秀开发人员,未检测到的错误/错别字的风险太大。对于其他所有人,风险更高。最好的开发人员是那些曾经记住该语言优先级规则的开发人员,但是由于习惯性地将括号用于任何不明显的事物而忘记了它们的优先级。
布伦丹2013年

31

您应该始终使用括号...您无法控制优先级顺序...编译器的开发人员可以。这是我发生的一个不使用括号的故事。这在两周内影响了数百人。

现实世界中的原因

我继承了一个大型机应用程序。有一天,它突然失去作用了。就这样... po声就停了下来。

我的工作是使它尽快运行。源代码已经有两年没有被修改,但是突然间它就停止了。我试图编译代码,但它在XX行上中断了。我看着XX行,但我不知道是什么使XX行中断。我询问了此应用程序的详细规格,但没有。XX行不是罪魁祸首。

我打印出了代码,并开始从上至下进行审查。我开始创建正在发生的事情的流程图。代码太复杂了,我什至无法理解。我放弃尝试绘制流程图。我很害怕做出更改,却不知道该更改将如何影响其余的过程,尤其是因为我没有应用程序的功能或依赖链中的位置的详细信息。

因此,我决定从源代码的顶部开始,并添加whitespce和行制动器,以使代码更具可读性。我注意到,在某些情况下,如果条件组合在一起ANDOR并且在AND编辑什么数据和编辑什么数据之间并没有明显的区别OR。因此,我开始在ANDOR条件周围加上括号,以使其更具可读性。

当我慢慢进行清理时,我会定期保存工作。有一次我尝试编译代码,然后发生了一件奇怪的事情。该错误已跳过原始代码行,而现在进一步降低了。所以我继续,用括号分隔了ANDOR条件。当我完成清理工作时。去搞清楚。

然后,我决定访问操作车间,询问他们最近是否在主机上安装了任何新组件。他们说是的,我们最近升级了编译器。嗯

事实证明,旧的编译器无论如何都从左到右评估表达式。新版本的编译器还从左到右评估表达式,但是代码含糊不清,这意味着不清楚的组合AND并且OR无法解决。

我从中学到的教训...总是,总是,总是使用括号将AND条件和OR条件相互结合使用。

简化示例

IF Product = 191 OR Product = 193 AND Model = "ABC" OR Product = 201 OR Product = 202 AND Model = "DEF" ...(其中的代码杂乱无章)

这是我遇到的问题的简化版本。复合布尔逻辑语句还有其他条件。

我记得将它赶到:
IF ((Product = 191 OR Product = 193) AND Model = "ABC") OR ((Product = 201 OR Product = 202) AND Model = "DEF") ...



我无法重写它,因为没有规范。原始作者早已不复存在。我记得压力很大。一整艘货船被困在港口,无法卸载,因为这个小程序不起作用。没有警告。无需更改源代码。在我发现添加括号改变了错误之后,我才问网络运营商是否修改了任何内容。


22
这似乎更好地说明了为什么拥有一个好的编译器很重要。
ruakh

6
@CapeCodGunny:我确定你没有。但是这里的问题是您的编译器供应商不好,并且做出了重大更改。即使您使用更好的编译器,我也看不到您如何从“总是,总是,总是”上学到解决该问题的课程。
ruakh 2013年

5
@ruakh-不,供应商只是更改了代码解析器。我是一所老学校,我并不依靠自己的能力来记住自己编写或参与的每个系统。没有文档,您所拥有的只是源代码。当难以阅读和遵循源代码时,就会造成极大的压力。不使用空格的程序员可能从未遇到过像我这样的情况。我还了解到编写如下代码更有意义:见简;见迪克和简;易于阅读的简单语句...注释掉并遵循。
迈克尔·赖利

2
很棒的故事,但我同意这不是使用括号的原因。和/或优先级几乎是普遍适用的,并且是人们可能想到的最不可能改变的事物。如果您不能依靠一致的行为来执行这种标准的基本操作,那么您的编译器将是垃圾,无论您做什么都将被束之高阁。您的故事是100%的编译器问题和0%的代码问题。尤其是考虑到默认行为是简单的从左到右求值-顺序总是很清楚,那么为什么有人会使用括号呢?

7
我认为双方都有经验教训……最好使用括号,尤其是当替代方法依赖于编译器针对歧义代码的未证明行为时。而且,如果程序员不知道哪些表达式是模棱两可的,最好使用parens。另一方面,更改编译器的行为很危险,但是至少在行为更改的情况下会引发错误。如果编译器没有引发错误,而是开始以不同的方式进行编译,那就更糟了。该错误使您免受未发现的错误的侵害。
LarsH 2013年

18

是的,如果混合使用“和”和“或”。

()逻辑上是一种检查也是个好主意。

尽管最好的方法是使用命名谓词函数,并在其中逐出大多数检查和条件,如果简单易懂,就保留它们。


6
没错,很有a AND b可能应该用具有描述性更强的名称的函数或预先计算的布尔值代替。
Jeff Bridgman 2013年

14

括号在语义上是多余的,因此编译器不在乎,但这是一个红色的鲱鱼-真正的问题是程序员的可读性和理解力。

我将在这里采取激进立场,并对中的括号表示诚挚的“不” a AND b OR c AND d。每个程序员都应该从心底知道布尔表达式的优先级不是NOT> AND> OR,就像记住“ 请为我亲爱的萨莉姨妈打扰 ”代数表达式一样。多余的标点符号大多数时候只会在代码中增加视觉混乱,而对程序员的可读性没有好处。

另外,如果您总是在逻辑和代数表达式中使用括号,那么您就放弃了使用它们作为标记的能力,因为“这里发生了一些棘手的事情-注意!” 也就是说,在您要覆盖默认优先级并在乘法运算之前或在与运算符AND之间进行加法运算的情况下,括号对下一个程序员来说是一个不错的警告。当不需要它们时,过多地使用它们,您就会成为哭狼的男孩。

对于代数领域之外的任何事物(不管是否为布尔值),例如C语言中的指针表达式,我都会作例外处理,其中比标准习语更复杂的事物(例如*p++p = p->next可能应加括号),以保持解引用和算术的直接性。而且,当然,这都不适用于使用某种形式的波兰语表示法的Lisp,Forth或Smalltalk等语言。但是对于大多数主流语言,逻辑和算术优先级是完全标准化的。


1
+1,为了清楚起见,括号是可以的,但是ANDvs OR是一个非常基本的情况,我希望团队中的其他开发人员都知道。我担心有时“使用括号以清楚起见”实际上是“使用括号,因此我不必费心学习优先级”。
Tim Goodman

1
在所有与我共事的语言经常是.member前一元运算符,一元二元之前运营商,*以及/之前+-之前<>==&&||转让前。这些规则很容易记住,因为它们符合我对正常使用运算符的“常识”(例如,您不会给出==比其更大的优先级+1 + 1 == 2停止工作),并且它们涵盖了我所拥有的95%的优先级问题。
蒂姆·古德曼

4
@TimGoodman是的,您的两个评论都正确。这里的其他响应者似乎认为这是一个黑人或白人问题-要么一直使用括号,没有例外,要么不顾一切地通过随意且难以记住的规则,在裤子的座位上随意飞行,代码泛滥成灾潜在的难以发现的错误。(混合的隐喻非常有意。)显然,正确的方法是节制。代码的清晰性很重要,但是对您的工具的了解也很重要,您应该能够从团队成员那里获得对编程和CS原理的某种最低程度的了解。
dodgethesteamroller 2013年

8

照我看来:

是的优点:

  • 操作顺序是明确的。
  • 保护您免受那些不了解操作顺序的未来开发人员的侵害。

缺点:

  • 可能导致混乱,难以阅读的代码

没有优点:

没有缺点:

  • 操作顺序是隐式的
  • 对于开发人员,如果不充分了解操作顺序,则代码的维护性较差。

说得好,尽管我认为“可能导致混乱,难以阅读的代码”相当主观。
FrustratedWithFormsDesigner 2013年

2
如何使代码的语义明确,使其难以阅读?
梅森·惠勒

1
我同意其他人对您“同意”的质疑。我认为几乎总是相反。

@沮丧与相反的主张一样主观。的意思,一点也不是真的。很难衡量。
Konrad Rudolph 2013年

1
@MasonWheeler:尽管我同意显式括号在许多情况下非常重要,但我可以看到人们如何过分强调语义。我发现3 * a^2 + 2 * b^2比起来更容易阅读(3 * (a^2)) + (2 * (b^2)),因为格式和优先级是熟悉且标准的。同样,您可能(极端)禁止使用函数和宏(或编译器!),以便使代码的语义更加明确。显然,我不主张这样做,但我希望回答您的问题,即为什么在使事情明确时需要限制(平衡)。
LarsH 2013年

3

是否有支持或反对包含多余括号的论点?实践经验是否表明值得将它们包括在内以提高可读性?还是表明开发人员需要真正坐下来并对他们的语言基础充满信心?

如果没有其他人不得不再次查看我的代码,我认为我不在乎。

但是,根据我的经验:

  • 我偶尔会再次查看我的代码(有时在写代码后的几年
  • 其他人有时看我的代码
    • 甚至必须扩展/修复它!
  • 我自己或其他人都不会完全记得我在写时的想法
  • 编写神秘的“减少字符数”代码会损害可读性

我几乎总是这样做,因为我相信我有能力快速阅读并且不会因小错误而犯更多的错误。

在您的情况下,我几乎可以肯定地说:

if (a AND b) then ab = true
if (c AND d) then cd = true
If (ab OR cd) Then ...

是的,这是更多代码。是的,我可以改做花式布尔运算符。不,我不希望将来再漏读1年以上的代码时会误读一些花哨的bool运算符。如果我用具有不同AND / OR优先级的语言编写代码并且不得不跳回以解决此问题怎么办?我要去吗,“啊哈!我还记得我所做的这个聪明的小事!去年写这篇文章时,我不必包括parens,现在我还记得好事!” 如果发生这种情况(或更糟的是,其他人没有意识到这种聪明之处,或者陷入了“尽快修复”类型的情况)?

用()分隔使得快速浏览和稍后了解变得更加直接...


7
如果要这样做,为什么不ab = a AND b呢?
埃里克

1
也许是因为ab如果没有,它将保持不变a AND b
阿玛利

3

一般情况

在C#中,乘法和除法优先于加法和减法。

尽管如此,StyleCop是一种在整个代码库中强制实施通用样式的工具,其附加目标是减轻可能不够清晰的代码引入的错误的风险,该工具具有规则SA1407。该规则将产生一条带有如下代码的警告:

var a = 1 + 2 * 3;

显然结果7不是9,但StyleCop仍建议加上括号:

var a = 1 + (2 * 3);

您的情况

在您的特定情况下,在您使用的特定语言中,AND优先于OR。

这不是每种语言的行为方式。许多其他人同样对待AND和OR。

作为主要使用C#的开发人员,当我第一次看到您的问题并在不阅读您之前编写的内容的情况下阅读代码时,我的第一个诱惑就是评论这两个表达式不相同。希望我在评论之前已经阅读了整个问题。

这种特殊性以及某些开发人员可能认为AND和OR具有相同优先级的风险使得添加括号变得更加重要。

不要以表明自己很聪明为目标而编写代码。编写代码以提高可读性为目标,包括可能不熟悉该语言各个方面的人员。


1
“这非常罕见”:根据Stroustrup2013,C ++ 11似乎具有不同的AND和OR优先级(第257页)。对于Python相同:docs.python.org/2/reference/expressions.html
Dirk

10
回复:“我知道的每种语言都将AND和OR等同对待”:我怀疑这是真的。如果它真实的,那么你不知道任何十个最流行的语言(C,Java和C ++,PHP,JavaScript中,Python和C#,Perl中,SQL和Ruby),并没有能力来评论什么是“罕见”,更不用说“非常罕见”了。
ruakh 2013年

1
我同意ruakh,并查找了C ++ 11,python,matlab和java。
德克(Dirk)2013年

3
AND和OR为二元运算符,并且不将AND的优先级视为比OR高的语言,是被人为破坏的胡言乱语,其作者是计算机科学的笨蛋。这来自逻辑符号。你好,“产品总和”不代表什么吗?甚至有一个布尔符号,它对AND使用乘法(因子的并置),对OR使用+符号。
卡兹(Kaz)2013年

3
@ruakh你只是为我讲了观点。因为存在一些病理性边缘情况,并不意味着您不应该学习标准的布尔优先级,并假定它成立,直到得到另外证明。这里我们不是在谈论任意的设计决策;布尔代数早于计算机而被发明。另外,请告诉我您正在谈论的Pascal规格。在此处此处在OR之前显示AND。
dodgethesteamroller 2013年


0

还是表明开发人员需要真正坐下来并对他们的语言基础充满信心?

如果您严格使用单数形式的语言,也许吧。现在将您所知道的所有语言,从古老到最现代,从编译到脚本编写再到SQL,再到您上个月发明的自己的DSL。

您还记得这些语言中每种语言的确切优先规则吗?


1
就像上面@Cape Cod Gunny指出的那样,即使您认为您知道这种语言很冷,编译器/运行时也可能会在您以下改变。
约旦

0

“即使在不必要的情况下,我也应该在逻辑语句中使用括号。”

是的,因为两个人会发现他们有帮助:

  • 下一位程序员的知识,能力或风格可能会有所不同

  • 将来您以后再返回此代码的将来!


-1

复杂的条件条件是“布尔代数”,您可以通过某种方式编写它们,使其看起来非常像代数,而您肯定会使用parens作为代数,对吗?

真正有用的规则是取反规则:

!(A || B) <=> !A && !B
!(A && B) <=> !A || !B

或者,以更清晰的格式:

!(A + B) <=> !A * !B
!(A * B) <=> !A + !B

这是真的显然只是代数时所写:

-1*(A + B) = -A + -B
-1*(A * B) = -A * -B

但是我们也可以将思想用于代数的简化和扩展:

(A && B) || (C && D) => 
((A && B) || C) && ((A && B) || D) => 
(AC && BC) && (AD && BD) =>
AC && BC && AD && BD

尽管在代码中您必须编写:

(A||C) && (B||C) && (A||D) && (B||D)

或者,以更清晰的格式:

(A + B) * (C + D) => 
((A + B) * C) + ((A + B) * D) => 
(AC + BC) + (AD + BD) =>
AC + BC + AD + BD

基本上,条件表达式仍然只是代数表达式,通过清楚地使用括号,您可以更轻松地将已知的各种代数规则应用于表达式,包括“简化或扩展此公式”的古老概念。


2
我不确定您实际上在回答这个问题,因为您以不一定是正确的假设来领导您的答案。我会用括号代数吗?并非一直如此。如果它有助于提高可读性,那么可以肯定,但是其他人已经在这里表达了这一点。并且将数学代数扩展为其他形式与编程并不完全一一对应-如果要检查几个字符串值的状态该怎么办?
德里克(Derek)2013年

如果相应的布尔代数足够复杂以允许使用括号,则您的条件运算可能应该使用括号。如果它很简单,不需要担保,那么您要么不必,要么不应该。无论哪种方式,都可以像对待数学表达式一样思考它,从而可以解决问题。
Narfanator 2013年

我上面的示例使用两个和四个布尔值。如果要检查两个或多个字符串值的状态,它将映射。每个检查都对应一个布尔变量;不管该检查是整数还是字符串相等;字符串,数组或哈希包含;复杂的表达式本身...没关系;重要的是,您在表达式中有不只一种对/错的度量。
Narfanator 2013年

2
就在挑剔,但!(A + B) <=> !A + !B-1*(A + B) = -A + -B不能运营商已经从翻转+*第二体现在哪里?
Jeff Bridgman

-1

即使括号是可选的,我也会使用括号,这是为什么,因为它有助于更​​好地为每个人,编写代码的人和准备看该代码的人理解。在您的情况下,即使布尔运算符也具有优先权,但一开始它可能会运行良好,但是我们不能说它会在每种情况下都对您有所帮助。所以我更喜欢在可能需要或可选的任何情况下使用用户括号。


-1

是。在任何情况下,当您认为自己的代码会更清晰时,都应该使用。请记住,您的代码应该足够清晰,以便其他人可以理解而无需阅读代码内的注释。因此,使用括号和花括号是一个好习惯。还请记住,这可能取决于您的公司/团队的特定实践。只需保持一种方法,不要混淆。

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.