如果条件A匹配,则条件B需要匹配才能执行操作C


148

我的问题是:

if (/* condition A */)
{
    if(/* condition B */)
      {
         /* do action C */
      }
    else
      /* ... */
}
else
{
   /* do action C */
}

是否可以只编写一次而不是编写两次动作C的代码?

如何简化呢?


56
将“动作C”的代码放在函数中?
CinCout

26
令人遗憾的是,这 HNQ并没有真正与C ++问题相关:/
YSC

2
谢谢大家对我的帮助!一开始,我只是想确保一切都很好,所以我使用了一个嵌套的if。因为这是我猜到的最简单的方法。下次问完问题后,我将尝试更多的努力。祝大家有个美好的一天:)
starf15h

13
这是一个非常好的策略:先编写可运行的代码,然后再担心使其优雅和高效。
法学徒

3
@Tim我确实将其发布为答案。在一个侧面说明,这是可悲的,看不太票那里。
CinCout

Answers:


400

解决此类问题的第一步始终是创建逻辑表。

A | B | Result
-------------------
T | T | do action C
T | F | ...
F | T | do action C
F | F | do action C

制作完表格后,解决方案就很清楚了。

if (A && !B) {
  ...
}
else {
  do action C
}

请注意,这种逻辑虽然较短,但对于将来的程序员而言可能很难维护。


35
我非常喜欢您显示了事实表,以帮助OP了解如何自己开发。您能否进一步讲解并解释如何从真值表中获取布尔表达式?对于刚接触编程和布尔逻辑的人来说,这可能根本不清楚。
法学徒

14
如果评估B有副作用,逻辑表必须考虑到这一点。
Yakk-Adam Nevraumont

79
@Yakk我的答案没有解决副作用,原因有两个。首先,该解决方案确实(偶然地)具有正确的副作用行为。其次,更重要的是,具有副作用的A和B可能是不好的代码,而关于这种边缘情况的讨论可能会从根本上分散有关布尔逻辑的问题。
QuestionC

52
可能值得注意的是,如果A && !B情况是“无操作”:!(A && !B)等效于!A || B这意味着您可以这样做if (!A || B) { /* do action C */ }并避免出现空块。
KRyan

54
如果if (A && !B)将来的程序员确实很难维护,那么实际上没有任何帮助。

65

您有两种选择:

  1. 编写一个执行“动作C”的函数。

  2. 重新排列逻辑,以使您没有那么多的嵌套if语句。问自己,什么情况导致“动作C”发生。在我看来,它发生在“条件B”为真或“条件A”为假时。我们可以将其写为“ NOT A OR B”。将其转换为C代码,我们得到

    if (!A || B) {
        action C
    } else {
        ...
    }
    

要了解有关此类表达式的更多信息,建议使用谷歌搜索“布尔代数”,“谓词逻辑”和“谓词演算”。这些是深刻的数学主题。您不需要学习所有内容,而只是基础知识。

您还应该了解“短路评估”。因此,表达式的顺序对于精确复制原始逻辑很重要。尽管B || !A在逻辑上是等效的,但使用Btrue 作为条件将与条件无关地执行“动作C” A


15
@Yakk参见德摩根定律。
法学徒

@ Code-Apprentice请原谅我糟糕的逻辑思维。我想问一下(!A || B)和(A &&!B)之间是否有任何区别。看来我的问题都可以。我的意思是您和QuestionC的方法。
starf15h

6
@ Starf15h还有一个关键的区别:执行“动作C”的位置。这种差异使我们的两个解决方案完全等效。我建议您在Google中搜索“ deMorgan的法律”,这应该有助于您了解这里发生的情况。
法学徒

5
这两种方案是完全等价的,但有可能是一个实际的差异取决于究竟...。如果什么都不做(即“如果满足这些条件,则执行C;否则,不执行任何操作”),则这显然是一种更好的解决方案,因为那时else可以简单地将语句完全排除在外。
Janus Bahs Jacquet

1
此外,根据A和B的名称,这种布置可能是更可读的或更少可读于人类比QuestionC的安排。
Michael-Clay Shirky在哪里,

15

您可以像这样简化语句:

if ((A && B) || (!A)) // or simplified to (!A || B) as suggested in comments
{
    do C
}

否则,将“ C”的代码放在单独的函数中并调用:

DoActionC()
{
    ....
    // code for Action C
}
if (condition A)
{
    if(condition B)
      {
         DoActionC(); // call the function
      }
    else
      ...
}
else
{
   DoActionC(); // call the function
}

7
或更简单地说if (!A || B)
-Tas

2
从逻辑上讲,(((A && B)||!A)等效于(B ||!A)
学徒制

@代码学徒B || !A将导致以true仅当Btrue,实际上并没有检查A,由于短路
CinCout

1
@CinCout好点。从理论布尔逻辑的观点来看,我的说法仍然正确,但我没有考虑短路布尔运算符的实用性。幸运的是,我自己的答案顺序正确。
法学徒

1
因此,从逻辑角度看,顺序并不重要。然而,从维护和可读性来看,有可能会因究竟是一个巨大的差异AB代表。
法学徒

14

在具有模式匹配的语言中,您可以以更直接地反映QuestionC答案中的真值表的方式来表达解决方案。

match (a,b) with
| (true,false) -> ...
| _ -> action c

如果您不熟悉语法,则每个模式都由|表示。后跟与(a,b)匹配的值,下划线用作通配符,表示“其他任何值”。因为除了动作c之外,我们唯一想做其他事情的情况是a为true而b为false时,我们将这些值明确声明为第一个模式(true,false),然后在这种情况下应做的事情。在所有其他情况下,我们陷入“通配符”模式并执行操作c。


10

问题陈述:

如果条件A匹配,则条件B需要匹配才能执行操作C

描述含义A暗含B,这是一个等同于逻辑命题的逻辑命题!A || B(如其他答案所述):

bool implies(bool p, bool q) { return !p || q; }

if (implies(/* condition A */,
            /* condition B */))
{
    /* do action C */
}

也许将其标记inline为C constexpr以及C ++?
einpoklum

@einpoklum我没有涉及其中的一些细节,因为这个问题并没有真正指定一种语言(但是给出了一个类似于C语法的示例),所以我给出了一个类似于C语法的答案。就我个人而言,我将使用宏,以便不会不必要地评估条件B。
jamesdlin

6

gh,这也让我感到震惊,但是正如Code-Apprentice指出的,我们保证需要do action C或运行nested- elseblock,因此可以将代码简化为:

if (not condition A or condition B) {
    do action C
} else {
    ...
}

这是我们处理3种情况的方式:

  1. 嵌套do action C在你的问题的逻辑要求condition A,并condition Btrue-在这样的逻辑,如果我们达到2 的术语if语句来那么我们就知道condition Atrue这样,所有我们需要评估的是,condition Btrue
  2. 嵌套else在你的问题的逻辑-阻塞要求condition Atruecondition Bfalse-我们能够达到的唯一方法else在这种逻辑-阻塞是,如果condition Atruecondition Bfalse
  3. else您问题逻辑中的外层块必须condition Afalse-在此逻辑中,如果condition A为假,我们也do action C

向代码学徒提出的建议,请他们在这里整理我。我建议接受他的答案,因为他正确地给出了答案而无需编辑:/


2
注意,“条件A”不需要再次评估。在C ++中,我们具有排除中间定律。如果“非条件A”为假,则“条件A”为真。
法学徒

1
由于存在短路评估,B因此只有在!A为假时才进行评估。因此,两者都必须失败才能执行else语句。
法学徒

即使没有短路评估,!A || B!AB均为假时,也恰好为假。因此,执行A时为true else。无需重新评估A
法学徒

@ Code-Apprentice很臭,很好的观察,我已经纠正了我的答案,但是建议您接受。我只是想解释一下您已经提出的内容。
乔纳森·梅

希望我能再给您投票以解释每种情况。
法学徒

6

在逻辑概念中,可以按以下方式解决此问题:

f = ab +!a
f =?

作为一个已证明的问题,这导致f = !a + b。有一些方法可以证明问题,例如真值表,卡诺地图等。

因此,在基于C的语言中,您可以按以下方式使用:

if(!a || b)
{
   // Do action C
}

PS:卡诺地图也可用于更复杂的一系列条件。这是一种简化布尔代数表达式的方法。


6

即使已经有了好的答案,我认为对于刚接触布尔代数然后评估真值表的人来说,这种方法可能更加直观。

你想要做的第一件事是看,在什么条件下要执行C.这是时的情况(a & b)。也当!a。所以你有(a & b) | !a

如果要最小化,可以继续。就像在“常规”算术中一样,您可以相乘。

(a & b) | !a = (a | !a) & (b | !a)。一个| !a始终是正确的,因此您可以将其删除,从而使结果最小化b | !a。如果顺序有所不同,因为仅当!a为真时才想检查b(例如,当!a是空指针检查并且b是对指针的操作,如@LordFarquaad在其注释中指出)时,您可能想同时切换两者。

其他情况(/ * ... * /)将始终在不执行c的情况下执行,因此我们可以将其放在else情况下。

另外值得一提的是,将动作c放入方法中的任何一种可能都有意义。

剩下的代码如下:

if (!A || B)
{
    doActionC()  // execute method which does action C
}
else
{
   /* ... */ // what ever happens here, you might want to put it into a method, too.
}

这样,您还可以使用更多操作数来最小化术语,而事实表会很快使它们变得丑陋。另一个好的方法是卡诺地图。但是我现在不会更深入地介绍这一点。


4

为了使代码看起来更像文本,请使用布尔标志。如果逻辑特别不清楚,请添加注释。

bool do_action_C;

// Determine whether we need to do action C or just do the "..." action
// If condition A is matched, condition B needs to be matched in order to do action C
if (/* condition A */)
{
    if(/* condition B */)
      do_action_C = true; // have to do action C because blah
    else
      do_action_C = false; // no need to do action C because blarg
}
else
{
  do_action_C = true; // A is false, so obviously have to do action C
}

if (do_action_C)
  {
     DoActionC(); // call the function
  }
else
  {
  ...
  }

3
if((A && B ) || !A)
{
  //do C
}
else if(!B)
{
  //...
}

2

我将C提取到方法中,然后在所有情况下尽快退出函数。else如果可能的话,结尾处只有一个东西的子句应该几乎总是被反转。这是一个分步示例:

提取物C:

if (A) {
   if (B)
      C();
   else
      D();
} else
   C();

首先反转if以摆脱第一else

if (!A) {
   C();
   return;
}

if (B)
   C();
else
   D();

摆脱第二else

if (!A) {
   C();
   return;
}

if (B) {
   C();
   return;
} 

D();

然后您会注意到这两种情况具有相同的主体并且可以组合:

if (!A || B) {
   C();
   return;
}

D();

可选的改进方法是:

  • 取决于上下文,但是如果!A || B令人困惑,请将其提取到一个或多个变量中以解释意图

  • 非例外情况中的哪一个C()或哪个D()应最后处理,因此,如果D()是例外情况,则if最后一次反转


2

使用标志也可以解决此问题

int flag = 1; 
if ( condition A ) {
    flag = 2;
    if( condition B ) {
        flag = 3;
    }
}
if(flag != 2) { 
    do action C 
}
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.