2

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

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

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

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

4

11 回答 11

9

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

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

...

return shift1.encompasses(shift2) || shift2.encompasses(shift1)
于 2010-01-15T21:12:28.360 回答
7

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

例如: if (x < y && y < z) 可以简化为 if (x < z)

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

于 2010-01-15T20:34:29.790 回答
4

尽管x < y && y < z隐含x < z(< 是可传递的),但反过来不成立,因此表达式不等价。事实上,如果y定义为 ,Integer y = null那么前者甚至可能导致 Java 中的 NPE 或 SQL 中的 UNKNOWN。

于 2010-01-15T20:34:35.697 回答
3

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

例如:

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;

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

于 2010-01-15T20:47:18.057 回答
3

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

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

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

优点:

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

缺点:

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

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

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

于 2010-01-15T22:06:38.730 回答
2

这样做时你需要非常小心......例如,你给出的例子根本不是真的......

如果x = 1, y = 2, 和z = 2, 那么x < y = true, 和x < z = true, 但是y < z = false.

对于这种类型的推理,你真的想在这些情况下追求代码的可读性,而不是担心你能得到的最有效的代码。

于 2010-01-15T20:35:32.073 回答
1

有时您可以包装语句,例如:

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);    
}
于 2010-01-15T20:44:19.140 回答
1

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

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

我们可以把它变成

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

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

于 2010-01-15T20:48:23.527 回答
0

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

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

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

于 2010-01-15T20:42:12.997 回答
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)
于 2010-01-15T21:03:19.550 回答
0

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

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

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

于 2010-01-15T21:16:56.373 回答