我的问题是:
if (/* condition A */)
{
if(/* condition B */)
{
/* do action C */
}
else
/* ... */
}
else
{
/* do action C */
}
是否可以一次而不是两次编写动作 C 的代码?
如何简化它?
在这类问题中,您的第一步始终是制作逻辑表。
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
}
请注意,此逻辑虽然较短,但对于未来的程序员可能难以维护。
你有两个选择:
编写一个执行“动作 C”的函数。重新排列你的逻辑,这样你就没有那么多嵌套的 if 语句。问问自己是什么条件导致“动作 C”发生。在我看来,当“条件 B”为真或“条件 A”为假时会发生这种情况。我们可以把它写成“NOT A OR B”。将其翻译成 C 代码,我们得到 if (!A || B) { action C } else { ... }
要了解有关此类表达式的更多信息,我建议在谷歌上搜索“布尔代数”、“谓词逻辑”和“谓词演算”。这些都是深刻的数学主题。您不需要全部学习,只需学习基础知识即可。
您还应该了解“短路评估”。因此,表达式的顺序对于准确复制原始逻辑很重要。虽然 B || !A
在逻辑上是等价的,但使用它作为条件将在 B
为真时执行“动作 C”,而不管 A
的值如何。
...
是什么。如果它什么都不是(即“如果满足这些条件,则执行 C;否则不执行任何操作”),那么这显然是更好的解决方案,因为此时可以简单地完全省略 else
语句。
您可以像这样简化语句:
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)
B || !A
仅当 B
为 true
时才会产生 true
,由于短路而不会实际检查 A
A
和 B
究竟代表什么。
在具有模式匹配的语言中,您可以以更直接地反映 QuestionC 答案中的真值表的方式表达解决方案。
match (a,b) with
| (true,false) -> ...
| _ -> action c
如果您不熟悉语法,每个模式都由 | 表示。后跟与 (a,b) 匹配的值,下划线用作通配符,表示“任何其他值”。因为我们想要做动作 c 以外的事情的唯一情况是当 a 为真而 b 为假时,我们明确地将这些值声明为第一个模式 (true,false),然后在这种情况下做任何应该做的事情。在所有其他情况下,我们都采用“通配符”模式并执行操作 c。
问题陈述:
如果条件 A 匹配,则需要匹配条件 B 才能执行操作 C
描述 implication: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++?
呃,这也让我绊倒了,但作为 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
为假时才会评估 B
。因此,两者都必须失败才能执行 else
语句。
!A
和 B
都为假时,!A || B
也是假的。因此,当 else
执行时,A
将为真。无需重新评估 A
。
在逻辑概念中,您可以通过以下方式解决此问题:
f = ab + !af = ?
作为一个已证明的问题,这会导致 f = !a + b
。证明问题的方法有真值表、Karnaugh Map等。
因此,在基于 C 的语言中,您可以按如下方式使用:
if(!a || b)
{
// Do action C
}
PS:Karnaugh Map 也用于更复杂的一系列条件。这是一种简化布尔代数表达式的方法。
尽管已经有了很好的答案,但我认为这种方法对于布尔代数的新手来说可能更直观,然后评估真值表。
您要做的第一件事是查看您要在什么条件下执行 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.
}
通过这种方式,您还可以使用更多操作数来最小化术语,而真值表很快就会变得丑陋。另一个好方法是卡诺图。但我现在不会更深入地讨论这个问题。
要使代码看起来更像文本,请使用布尔标志。如果逻辑特别模糊,请添加注释。
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
{
...
}
if((A && B ) || !A)
{
//do C
}
else if(!B)
{
//...
}
我会将 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
使用标志也可以解决这个问题
int flag = 1;
if ( condition A ) {
flag = 2;
if( condition B ) {
flag = 3;
}
}
if(flag != 2) {
do action C
}
不定期副业成功案例分享
B
有副作用,则逻辑表必须考虑到这一点。A && !B
是空操作:!(A && !B)
等价于!A || B
,这意味着您可以执行if (!A || B) { /* do action C */ }
并避免出现空块。if (A && !B)
对未来的程序员来说真的很难维护,那么就真的无济于事了。