简化if语句逻辑

时间:2010-01-15 20:31:29

标签: language-agnostic readability boolean-logic

我已经分开了一项测试,以确定两个计划项目是否重叠,因为它的不可读性。

是否有任何应用程序可以帮助简化逻辑陈述?

示例:(最初是一个错误的例子,但是暴露了我要求的原因)

if (x < y && y < z && x < z)  

可以缩减为

if (x < y && y < z)

我的代码是:

return (shift.Start <= shift2.Start && shift.End >= shift2.End) || (shift2.Start <= shift.Start && shift2.End >= shift.Start)

我希望能够更简单,我相信这是可能的,只是不确定如何。

看到这确实是语言不可知的,即使转换到不同的脚本来寻找可能性也会很好,例如,不需要它在C#中。

11 个答案:

答案 0 :(得分:7)

杀死重复的逻辑,你将一石二鸟。你会得到干,你会得到一个功能名称(富人的评论):

class Shift:
  def encompasses(other_shift)
    self.start <= other_shift.start && self.end >= other_shift.end

...

return shift1.encompasses(shift2) || shift2.encompasses(shift1)

答案 1 :(得分:6)

对这些变化非常非常小心。它们看起来很简单,而且布尔逻辑(和DeMorgan的定律)并不难理解,但是当你看到个别情况时,往往会有潜在的陷阱:

  

例如:if(x&lt; y&amp;&amp; y&lt; z)可以简化为if(x&lt; z)

这是不正确的,如果(x < z)y可能仍然大于z。该状态不会通过原始测试。

答案 2 :(得分:4)

虽然x < y && y < z暗示x < z(&lt;是传递性的),但反之则不然,因此表达式不相等。实际上,如果将y定义为Integer y = null,则前者甚至可能导致Java中的NPE或SQL中的UNKNOWN。

答案 3 :(得分:3)

  

是否有任何应用程序可以帮助简化逻辑陈述?

我没有看到有人解决这个问题的这一部分,所以我会采取刺,看看会发生什么讨论。

有一些使用布尔逻辑的技术。回到我的大学时代(BSEE),我们使用了Karnaugh maps。基本上,您可以采用非常复杂的任意真值表并确定正确和优化的逻辑表达式。我们用它来减少电路中逻辑门的数量,这类似于简化复杂的if语句。

优点:

  • 您可以相对轻松地实现/优化非常复杂和任意的真值表。

缺点:

  • 由此产生的逻辑通常与真值表的意图几乎没有相似之处。正如其他人所说,这是“难以理解的”。
  • 对真值表的单个单元格的更改通常会导致完全不同的表达式。一个简单的设计调整变成了重写,所以它是不可维护的。
  • 非优化逻辑比以前便宜很多,而设计成本却相同。

最终,最关键的是真值表/逻辑表达式的正确性。错误意味着您的程序无法正常工作。如果您没有正确理解需要实现的逻辑的定义,那么任何应用程序或设计技术都无济于事。

在我看来,很少有现实问题足够复杂,无法真正从这种技术中受益,但确实存在。

答案 4 :(得分:2)

这样做时你需要非常小心...例如,你给出的例子不是真的......如果x = 1,y = 2,z = 2,那么x&lt; y =真,x < z =真,但y < z =假。对于这种类型的推理,你真的想在这些情况下寻求代码可读性,而不用担心你能得到的最有效的代码。

答案 5 :(得分:2)

当谈到复杂的逻辑语句时,通常最好以可读的方式格式化代码而不是尝试一些过早的优化(所有邪恶的根源等)。

例如:

return (shift.Start <= shift2.Start && shift.End >= shift2.End) || (shift2.Start <= shift.StartTime && shift2.End >= shift.Start)

为了便于阅读和维护,可以重构为:

bool bRetVal = false;
bRetVal = (    (   (shift.Start <= shift2.Start)
                && (shift.End >= shift2.End))
            || (   (shift2.Start <= shift.StartTime)
                && (shift2.End >= shift.Start)))
return bRetVal;

大多数地方都维护着一个编码标准,它为大型逻辑块定义了类似的内容。我宁愿保留一些额外的代码行,这些代码可以读取和理解,而不是一行的怪物。

答案 6 :(得分:1)

有时你可以包装诸如

之类的陈述

shift.Start&lt; = shift2.Start&amp;&amp; shift.End&gt; = shift2.End

进入布尔函数以使其更具可读性,例如:

功能ShiftWithinValidRange(这里有可怕的名字,但你明白了)
{
  返回(shift.Start&lt; = shift2.Start&amp;&amp; shift.End&gt; = shift2.End);
}

答案 7 :(得分:1)

假设Start和StartTime实际上应该是同一个字段,那么你的条件归结为

(a <= b && c >= d) || (b <= a && d >= c)

我们可以把它变成

(a <= b && d <= c) || (b <= a && c <= d)

但这仍然看起来不像它简化了很多。

答案 8 :(得分:0)

这不仅危险,而且通常会导致难以维护代码。分解为特定步骤时,布尔逻辑更容易理解。压缩逻辑通常会导致更难理解逻辑。

即。在您的示例中,我们为什么要检查x < z,我们真正想知道的是x < y && y < z

最简单的解决方案往往是最好的解决方案。从长远来看,将你的逻辑压缩成“更酷”但代码不太可读的代码并不好。

答案 9 :(得分:0)

我没有一个通用的解决方案,但如果我使用Lisp语法,对我来说看起来要简单得多:

(and (< x y)
     (< y z)
     (< x z))

然后注意前两个条款:

(and (< x y)
     (< y z))

可以组合成:

(and (< x y z))

所以完整的表达式现在看起来像:

(and (< x y z)
     (< x z))

现在很明显,第二个是多余的,所以它归结为:

(and (< x y z))

或简单地说:

(< x y z)

在C语法中是:

(x < y && y < z)

答案 10 :(得分:0)

我认为Wayne Conrad的答案是正确的,但仅出于娱乐目的,这是另一种说法(我认为):

(long) shift.Start.CompareTo(shift2.Start) * (long) shift.End.CompareTo(shift2.End) <= 0

这实际上更快吗?我不知道。这当然更难阅读。