【问题标题】:If condition A is matched, condition B needs to be matched in order to do action C如果条件 A 匹配,则需要匹配条件 B 才能执行操作 C
【发布时间】:2017-12-25 13:07:36
【问题描述】:

我的问题是:

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

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

如何简化?

【问题讨论】:

  • 把“action C”的代码放到一个函数中?
  • 与 C++ 没有真正关系问题到 HNQ 很遗憾:/
  • 感谢大家帮助我!一开始,我只是想确保一切正常,所以我使用了嵌套的 if。这是因为这是我猜到的最简单的方法。下次提问后我会努力加倍努力。祝大家有个美好的一天:)
  • 这是一个非常好的策略:先编写可以运行的代码,然后再考虑使其优雅高效。
  • @Tim 我确实将其发布为答案。在旁注中,看到那里的选票减少令人遗憾。

标签: if-statement language-agnostic conditional-statements boolean-logic


【解决方案1】:

解决这类问题的第一步始终是制作逻辑表。

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
}

请注意,此逻辑虽然较短,但对于未来的程序员可能难以维护。

【讨论】:

  • 我真的很喜欢你展示了真值表来帮助 OP 了解如何自己开发它。你能更进一步解释一下你是如何从真值表中得到布尔表达式的吗?对于刚接触编程和布尔逻辑的人来说,这可能根本不清楚。
  • 如果评估 B 有副作用,则逻辑表必须考虑到这一点。
  • @Yakk 我的回答没有解决副作用有两个原因。首先,该解决方案确实(巧合地)具有正确的副作用行为。其次,更重要的是,具有副作用的 A 和 B 将是糟糕的代码,而关于这种边缘情况的讨论将分散从根本上关于布尔逻辑的问题的注意力。
  • 也许值得注意的是,如果A && !B 是空操作:!(A && !B) 等效于!A || B,这意味着您可以执行if (!A || B) { /* do action C */ } 并避免出现空块。跨度>
  • 如果if (A && !B) 对未来的程序员来说真的很难维护,那真的没有帮助。
【解决方案2】:

你有两个选择:

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

  2. 重新排列你的逻辑,这样你就没有那么多嵌套的 if 语句。问问自己是什么条件导致“动作 C”发生。在我看来,当“条件 B”为真或“条件 A”为假时会发生这种情况。我们可以把它写成“NOT A OR B”。将其翻译成 C 代码,我们得到

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

要了解有关此类表达式的更多信息,我建议您搜索“布尔代数”、“谓词逻辑”和“谓词演算”。这些都是深刻的数学主题。您无需全部学习,只需了解基础知识即可。

您还应该了解“短路评估”。因此,表达式的顺序对于准确复制原始逻辑很重要。虽然B || !A 在逻辑上是等价的,但是当B 为真时,无论A 的值如何,使用它作为条件都会执行“动作C”。

【讨论】:

  • @Yakk 参见德摩根定律。
  • @Code-Apprentice 请原谅我糟糕的逻辑思维。我想问一下 (!A || B) 和 (A && !B) 之间是否有任何区别。对于我的问题,似乎两者都可以。我的意思是你和 QuestionC 的方法。
  • @Starf15h 还有一个重要的区别:“动作 C”的执行位置。这种差异使我们的两个解决方案完全等效。我建议你用谷歌搜索“deMorgan's Laws”,它应该可以帮助你了解这里发生了什么。
  • 这两种解决方案完全相同,但可能存在实际差异取决于... 究竟是什么。如果它什么都不是(即“如果满足这些条件,则执行 C;否则不执行任何操作”),那么这显然是更好的解决方案,因为 else 语句可以完全省略。
  • 另外,根据 A 和 B 的名称,to a human 的这种排列可能比 QuestionC 的排列更具可读性或可读性。
【解决方案3】:

你可以这样简化语句:

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
}

【讨论】:

  • 或者更简单的if (!A || B)
  • 逻辑上,((A&&B) || !A) 等价于 (B || !A)
  • @Code-Apprentice B || !A 仅当Btrue 时才会产生true,由于短路而不会实际检查A
  • @CinCout 好点。虽然从理论布尔逻辑的角度来看,我的陈述仍然正确,但我没有考虑短路布尔运算符的实用性。幸运的是,我自己的答案顺序正确。
  • 所以从逻辑的角度来看,顺序并不重要。但是,从维护和可读性的角度来看,可能存在巨大差异,具体取决于 AB 究竟代表什么。
【解决方案4】:

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

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

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

【讨论】:

    【解决方案5】:

    问题陈述:

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

    描述 implicationA 隐含 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 我没有深入其中的一些细节,因为这个问题并没有真正指定一种语言(但给出了一个类似 C 语法的例子),所以我用 C- 给出了答案像语法。就我个人而言,我会使用一个宏,这样就不会对条件 B 进行不必要的评估。
    【解决方案6】:

    呃,这也让我感到困惑,但作为pointed out by Code-Apprentice,我们保证需要do action C 或运行嵌套-else 块,因此代码可以简化为:

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

    这就是我们处理这 3 个案例的方式:

    1. 问题逻辑中嵌套的do action C 要求condition Acondition Btrue -- 在此逻辑中,如果我们到达if 中的第二个nd 项-statement 然后我们知道condition Atrue 因此我们需要评估的是condition Btrue
    2. 问题逻辑中嵌套的else-block 要求condition Atruecondition Bfalse——在此逻辑中我们可以到达else-block 的唯一方法如果condition Atruecondition Bfalse
    3. 问题逻辑中的外部else-block 要求condition Afalse -- 在此逻辑中,如果condition A 为假,我们也do action C

    对 Code-Apprentice 的支持,让我在这里理顺。我建议接受his answer,因为他在没有编辑的情况下正确地展示了它:/

    【讨论】:

    • 请注意,“条件A”不需要再次求值。在 C++ 中,我们有排中定律。如果“非条件 A”为假,则“条件 A”必然为真。
    • 由于短路评估,仅当!A 为假时才会评估B。所以两者都必须失败才能执行else 语句。
    • 即使没有短路评估 !A || B!AB 都为假时为假。因此,A 将在 else 执行时为真。无需重新评估A
    • @Code-Apprentice 好臭,观察力很好,我已经更正了我的答案,但建议你接受。我只是想解释一下你已经提出的内容。
    • 我希望我能给你另一个投票来解释每个案例。
    【解决方案7】:

    在逻辑概念上,你可以这样解决这个问题:

    f = a.b + !a
    f = ?

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

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

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

    P.S.:Karnaugh Map 也用于更复杂的一系列条件。 这是一种简化布尔代数表达式的方法。

    【讨论】:

      【解决方案8】:

      尽管已经有了很好的答案,但我认为这种方法对于布尔代数新手来说可能更直观,然后评估真值表。

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

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

      (a & b) | !a = (a | !a) & (b | !a)。 一个 | !a 始终为真,因此您可以将其划掉,从而得到最小化的结果:b | !a。 如果顺序有所不同,因为您只想在 !a 为真时检查 b (例如,当 !a 是空指针检查并且 b 是像@LordFarquaad 在他的评论中指出的那样对指针进行操作时),您可能想切换两者。

      other case (/* ... */) is 总是在 c 不执行时执行,所以我们可以把它放在 else case 中。

      另外值得一提的是,将动作 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.
      }
      

      通过这种方式,您还可以使用更多操作数来最小化术语,而真值表很快就会变得丑陋。另一个好方法是卡诺图。但我现在不会深入探讨。

      【讨论】:

        【解决方案9】:

        要使代码看起来更像文本,请使用布尔标志。如果逻辑特别晦涩,请添加 cmets。

        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
          {
          ...
          }
        

        【讨论】:

          【解决方案10】:
          if((A && B ) || !A)
          {
            //do C
          }
          else if(!B)
          {
            //...
          }
          

          【讨论】:

            【解决方案11】:

            我会将 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();
            

            然后你可以注意到这两个case有相同的body并且可以合并:

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

            可选的改进是:

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

            • C()D() 中的任何一个是非异常情况都应该放在最后,所以如果 D() 是异常情况,那么最后一次反转 if

            【讨论】:

              【解决方案12】:

              使用标志也可以解决这个问题

              int flag = 1; 
              if ( condition A ) {
                  flag = 2;
                  if( condition B ) {
                      flag = 3;
                  }
              }
              if(flag != 2) { 
                  do action C 
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2011-04-20
                • 2019-06-24
                • 2011-07-18
                • 1970-01-01
                • 2021-10-18
                • 1970-01-01
                • 2018-02-24
                • 2021-06-15
                相关资源
                最近更新 更多