【问题标题】:Simplifying if statement logic简化 if 语句逻辑
【发布时间】:2010-01-15 20:31:28
【问题描述】:

我已经分离出一个测试来确定两个计划项目是否因为不可读而重叠。

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

示例:(最初是一个错误的示例,但公开了我要求这样做的原因)

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# 中。

【问题讨论】:

  • (x
  • 为最初的错误示例向大家道歉,已修复。
  • 我很好奇的一件事是shift.StartTimeshift.Start 之间的区别是什么。我试图为布尔变量提供注释或名称,这可能有助于记录表达式的意图,但被这个细节所阻碍。
  • 啊,原始代码中有两个不同的类,一个使用 .StartTime 另一个使用 .Start 由于我这样做是为了示例,因此我试图使它们彼此一致。

标签: language-agnostic readability boolean-logic


【解决方案1】:

杀死重复的逻辑,您将用一块石头杀死两只鸟。你会得到 DRY,你会得到一个函数名(富人的评论):

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

...

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

【讨论】:

  • 打败我。要考虑的另一件事是避免使用名为shiftshift2 的变量,因为它会使长代码变得更长且更重复。随时给我a.covers(b) || b.covers(a)
  • 不幸的是,这不会涵盖一个班次在另一个班次之前开始并在另一个班次开始之后但在它结束之前结束的情况。它不是包含我正在寻找的,而是重叠的。
【解决方案2】:

对这些变化要非常非常小心。乍一看,它们可能看起来很简单,布尔逻辑(和德摩根定律)并不难掌握,但是当您查看个别案例时,通常会存在潜在的陷阱:

例如:if (x

这是不正确的,如果(x &lt; z)y 可能仍然大于z。该状态不会通过您最初的测试。

【讨论】:

    【解决方案3】:

    虽然x &lt; y &amp;&amp; y &lt; z 暗示x &lt; z(y 被定义为Integer y = null,那么前者甚至可能在 Java 中导致 NPE,或者在 SQL 中导致 UNKNOWN。

    【讨论】:

      【解决方案4】:

      当涉及到复杂的逻辑语句时,您通常最好以可读的方式格式化您的代码,而不是尝试一些过早的优化(万恶之源等)

      例如:

      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;
      

      大多数地方都维护一个编码标准,该标准为大型逻辑块定义了上述内容。我宁愿保留几行可以阅读和理解的额外代码,而不是单行怪物。

      【讨论】:

      • 可读性是简化而不是优化的最初目标 :) 非常好的主意,缺少一些数学魔术应用程序来简化布尔逻辑以便于阅读,我想这样就可以了。
      • 如果为了便于阅读,请使用:stackoverflow.com/questions/2074572/… 请记住,最终必须有人维护您的代码,并且以原始方式或这种方式,很难理解这里发生了什么。
      【解决方案5】:

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

      我没有看到有人解决这部分问题,所以我会尝试一下,看看会发生什么讨论。

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

      优点:

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

      缺点:

      • 生成的逻辑通常与真值表的意图几乎没有相似之处。正如其他人所建议的那样,这是“不可读的”。
      • 更改真值表的单个单元格通常会导致完全不同的表达式。简单的设计调整变成了重写,因此无法维护。
      • 未优化的逻辑比以前便宜很多,而设计成本相同。

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

      在我看来,现实世界中很少有复杂的问题能够真正受益于这种技术,但它确实存在。

      【讨论】:

        【解决方案6】:

        这样做时您需要非常小心...例如,您给出的示例根本不正确...

        如果x = 1y = 2z = 2,则x &lt; y = truex &lt; z = true,但y &lt; z = false

        对于这种类型的推理,您确实希望在这些情况下追求代码的可读性,而不是担心您可以获得最有效的代码。

        【讨论】:

        • 代码可读性是我想要获得的,因为我的代码中复杂的 if 语句是一场噩梦,我需要在其中修复错误,并希望它可以写得更简单.
        【解决方案7】:

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

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

        我们可以把它变成

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

        但这看起来仍然没有简化多少。

        【讨论】:

        • +1 寻求帮助,Matt Jordan 和您的回答都构成了我的问题的解决方案。
        【解决方案8】:

        有时您可以包装如下语句:

        shift.Start <= shift2.Start && shift.End >= shift2.End
        

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

        function ShiftWithinValidRange //(terrible name here, but you get the idea)
        {
            return (shift.Start <= shift2.Start && shift.End >= shift2.End);    
        }
        

        【讨论】:

        • 这就是我开始做的事情,然后逻辑中出现了错误,花了一段时间才弄清楚要改变什么。
        【解决方案9】:

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

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

        最简单的解决方案往往是最好的解决方案。从长远来看,将您的逻辑浓缩为“更酷”但可读性较差的代码并不好。

        【讨论】:

          【解决方案10】:

          我没有适合你的通用解决方案,但如果我使用 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)
          

          【讨论】:

            【解决方案11】:

            我认为韦恩康拉德的答案是正确的,但仅出于娱乐目的,这里有另一种说法(我认为):

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

            这实际上更快吗?我不知道。它肯定更难阅读。

            【讨论】:

            • 这只有在我们可以假设一个转变在它结束后永远不会开始的情况下才有效......这看起来很合理,但你永远不会知道。 :)
            猜你喜欢
            • 1970-01-01
            • 2011-09-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多