ChatGPT解决这个技术问题 Extra ChatGPT

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

我的问题是:

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

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

如何简化它?

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

Q
QuestionC

在这类问题中,您的第一步始终是制作逻辑表。

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) 对未来的程序员来说真的很难维护,那么就真的无济于事了。
C
Code-Apprentice

你有两个选择:

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

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

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


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

您可以像这样简化语句:

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 究竟代表什么。
A
Aaron M. Eshbach

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

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

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


j
jamesdlin

问题陈述:

如果条件 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。
J
Jonathan Mee

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

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

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

您问题逻辑中的嵌套 do 操作 C 要求条件 A 和条件 B 为真——在这个逻辑中,如果我们到达 if 语句中的第二项,那么我们知道条件 A 为真,因此我们需要评估的只是条件 B 为真 您问题逻辑中的嵌套 else 块要求条件 A 为真,条件 B 为假——我们可以在此逻辑中到达 else 块的唯一方法是条件 A 为真并且条件 B 为假 问题逻辑中的外部 else 块要求条件 A 为假——在此逻辑中,如果条件 A 为假,我们也执行操作 C

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


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

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

f = ab + !af = ?

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

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

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

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


d
deetz

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

您要做的第一件事是查看您要在什么条件下执行 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.
}

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


a
anatolyg

要使代码看起来更像文本,请使用布尔标志。如果逻辑特别模糊,请添加注释。

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

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

D
Dave Cousineau

我会将 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


s
styvane

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

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