样式化复合AND / OR if语句


13

如何为复杂的复合AND / OR if语句设置样式,以实现最大的可读性?您如何缩进以及将换行符放在何处?我的特殊情况如下。绝对比将所有内容都粉碎成一条线要好,但是看起来仍然很凌乱。

if (
    (
        x == y
        && a != b
        && p.isGood() 
        && (
            i + u == b
            || q >= a
        )
    )
    || k.isSomething()
    || m > n
) {
    doSomething();
}

1
愚蠢的缩进和括号/括号结构是故意的还是样式的一部分?
Ed S.

滑稽。一周前,我在SO上问了同样的问题,但此问题已经关闭。很高兴看到这个问题仍然存在!
埃里克·贝莱尔

Answers:


6

为每个小步骤设置布尔变量:

bool step1 = i + u == b || q >= a;
bool step2 = a != b && p.isGood() && group1;
bool step3 = group2 || k.isSomething() || m > n;
if (step3) { doSomething(); }

当然,这与泪谱学的答案类似,只是每个步骤的名称不同。

如果你的名字step1step2step3以多种方式让好的概念的意义上,这应该是迄今为止最清晰可辨。p.isGood()并且k.isSomething()有时在它不会是你的原代码的情况下,如果你在一个非常紧密的循环中运行这段代码中调用,所以这不会是一个选项,如果这些功能是昂贵的或。

另一方面,您不必担心创建新变量可能会对性能造成的影响。一个好的编译器会优化它们。

带有矩形碰撞检测的示例(由于上述性能问题,您可能不会使用):

if((a.x + a.width >= b.x || b.x + b.width >= a.x)
 && (a.y + a.height >= b.y || b.y + b.width >= a.y)
)
{ collision(); }

可能成为:

bool horizMatch = a.x + a.width >= b.x || b.x + b.width >= a.x;
bool vertMatch = a.y + a.height >= b.y || b.y + b.width >= a.y;
if(horizMatch && vertMatch) { collision(); }

另外,如果您想保留代码,我也认为完全可以。老实说,我认为您的代码清晰易读。显然我不知道到底a b x y i u p k m n是什么,但是就结构而言,对我来说看起来不错。


8

如果条件变得如此复杂,我通常会将代码重构为更加模块化的代码。


在这种情况下,我会避免屈服。单独测试这么小的函数是很愚蠢的,并且在函数之外悬挂定义会使代码不那么自显。
宫阪丽

这取决于您的重构程度。我认为,使用有意义的函数名,而不是看起来像代数教科书的代码串那样的条件字符串,会使代码更加自言自语。
JohnFx 2011年

但是从外观上看,您的函数会悬在外面,并且函数的确切作用不会立即显而易见。在您向上滚动之前,它暂时是一个黑匣子。除非您使用的语言允许在函数中使用函数,否则无论您的屈光度如何,对于读者或作者来说,这都不是很方便的。而且,如果您使用的语言允许函数使用函数,那么语法与声明绑定或变量(例如let x = a > b或)几乎没有什么不同let f a b = a > b
宫阪丽

变量将同样有效。我也会考虑重构。
JohnFx 2011年

啊好吧。
宫阪丽

8

在这种复杂程度下,我会做更多类似的事情

bool doIt = x == y && a != b && p.isGood();
doIt &= ( i + u == b || q >= a);
doIt |= k.isSomething() || m > n;

if(doIt)
{
    doSomething();
}

它很丑陋,但是可读性强,我敢肯定编译器会知道如何重构它。

另一方面,如果我曾经遇到过编写这样的IF语句的情况,我会重新考虑解决方案,因为我确定有一种更简单的方法,或者至少可以抽象出某些条件(例如:x == y && a != b && p.isGood()真的只是刻薄this->isPolygon(),我可以采用这种方法;


4

随着时间的流逝,我对垂直对齐的痴迷程度降低了,但是我对多行表达式的一般形式是...

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6)
       )
    && (   (expr7 == expr8)
        || (expr9 == expra)
       )
   )
{
  blah;
}

关键点...

  • 闭合的括号和括号一起与打开的括号垂直对齐。
  • 一条线内的子表达式位于一条线内,并且在左侧垂直对齐。在提高可读性的地方,这些单行部分中的中缀运算符也垂直对齐。
  • 右括号自然地产生了近乎空白的线条,有助于视觉上对事物进行分组。

有时,我也会格式化+和/ *或其他一些类似的运算符。相当多的复杂表达式采用乘积和或乘积形式(可以指布尔“和”和“乘积”),因此它很常见,因此值得使用一致的样式。

不过要小心。通常最好进行重构(将表达式的一部分移至函数中,或将中间部分计算并存储在变量中),而不是使用缩进来尝试使过于复杂的表达式更具可读性。

如果您喜欢在右侧堆叠您的近亲,我不会讨厌它,但是我想它还不错。太远了,您冒着这样的风险,即错误可能使缩进错误地表示括号的作用。

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6))

    && (   (expr7 == expr8)
        || (expr9 == expra)))
{
  blah;
}

+1我喜欢你的造型。它直接回答了我的问题,但我认为宫阪丽确定了问题的根源。如果我懒于执行Rei的方法,我将使用您的样式。
JoJo

哇,真是太好了。
宫阪丽

1

http://www.codinghorror.com/blog/2006/01/flattening-arrow-code.html

我同意JohnFx以及Lacrymology的回答。我将构建一堆实现小目标的功能(最好是静态的),然后以一种聪明的方式在这些功能上进行构建。

那么,这样的事情怎么样?注意,这不是完美的解决方案,但它可以工作。有一些方法可以进一步清理此问题,但是需要更具体的信息。注意:由于编译器很聪明,因此此代码应以相同的速度运行。

// Currently based on members or global vars
// (which is often a bad idea too)
function doSomethingCondirionally()
{
  if (k.isSomething() || m > n)
  {
    doSomething();
    return;
  }

  // Else ... 
  if (x != y) return;
  if (a == b) return;
  if (!p.isGood()) return;

  // Final, positive check
  if (i + u == b || q >= a)
  {
    doSomething();
  }
}

如果您仅珍惜某个功能的一个退出点(例如,如果您使用的是功能语言),那么这可能不是最佳选择,甚至不是可用的选择。就是说,是的,这就是我经常做的事。
宫阪丽

如果是像Javascript这样的解释性语言怎么办?
JoJo

@宫坂丽,我对它的重视程度取决于语言。虽然我喜欢LISP语言家族,但我不必在工作中使用一种语言。如果我必须重构其他人的功能,但又不碰其他代码(通常是现实的话),那么我会做上面的事情。如果我可以从头开始编写/重写此逻辑,那么我的方法将有所不同,但是如果没有作者在此处尝试做的具体示例,就无法编写这样的代码。
工作

1
@宫坂丽(Rei Miyasaka),那个人可能是个天才,或者是个胡扯。我什么都不知道,但是我很想知道那个人对单出口的辩护。在这里和其他地方都对此进行了讨论,我的印象是,这种方法也许在80年代受到了学者的欢迎,但不再重要,并且实际上会妨碍可读性。当然,如果您以LINQ功能风格进行所有操作,则甚至不会出现此问题。
工作

2
@Job @Steve我认为这在需要显式释放的语言中是一个更重要的准则。显然不是每个功能都可以,但是这可能是鼓励初学者程序员养成的一种习惯,以避免忘记释放资源。
宫阪丽

1

对于它的价值,我很惊讶地看到您的示例看起来很像我编写的复杂谓词。我同意其他人的观点,即复杂的谓词对于可维护性或可读性而言并不是最大的选择,但有时会出现。

让我强调您已正确完成了此部分:&& a != b 切勿将逻辑连接器放在行尾,这很容易在视觉上遗漏。永远不要在行的末尾放置运算符的另一个地方是字符串连接,使用这种运算符的语言。

做这个:

String a = b
   + "something"
   + c
   ;

不要这样做:

String a = b +
   "something" +
   c;

您是否有任何逻辑或研究支持您对逻辑连接器的断言,或者仅仅是您的喜好陈述为事实?我在很多地方都听到过很多,但从未理解过(与yoda条件句不同,后者有合理的[如果被误解]原因)。
Caleb Huitt-cjhuitt 2011年

@Caleb-数个世纪以来,以这种方式排版数学。浏览代码时,我们将重点放在每行的左侧。以运算符开头的行显然是前一行的延续,而不是不正确缩进的新语句。
凯文·克莱恩

我也喜欢数学运算符的前缀。我意识到自己的编程生涯很​​晚:)
JoJo

0

如果条件是如此复杂,则通常表明应将其分解为多个部分。也许可以将一个子句分配给中间变量。也许一个子句可以变成辅助方法。我通常不希望一行中有那么多的“ and”或“ ors”。


0

您可以将代码分成多个语句,以使其易于理解。但是真正的忍者会做这样的事情。:-)

if
(
    (
        x == y
    &&
        a != b
    &&
        p.isGood()
    &&
        (
            i + u == b
        ||
            q >= a
        )
    )
||
    k.isSomething()
||
    m > n
)
{
    doSomething();
}

5
我是空白的爱好者,但是为了我的口味,这几乎用空白行填补了。
Steve314 2011年
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.