如何改进逻辑,检查4个布尔值是否匹配某些情况

我有四个 bool值:

bool bValue1;
bool bValue2;
bool bValue3;
bool bValue4;

可接受的数值如下:

         Scenario 1 | Scenario 2 | Scenario 3
bValue1: true       | true       | true
bValue2: true       | true       | false
bValue3: true       | true       | false
bValue4: true       | false      | false

So, for example, this scenario is not acceptable:

bValue1: false
bValue2: true
bValue3: true
bValue4: true

目前,我提出这个 if声明是为了检测糟糕的情况:

if(((bValue4 && (!bValue3 || !bValue2 || !bValue1)) ||
((bValue3 && (!bValue2 || !bValue1)) ||
(bValue2 && !bValue1) ||
(!bValue1 && !bValue2 && !bValue3 && !bValue4))
{
// There is some error
}

语句逻辑是否可以改进或简化?

21515 次浏览

正如 mch 所建议的,你可以这样做:

if(!((bValue1 && bValue2 && bValue3) ||
(bValue1 && !bValue2 && !bValue3 && !bValue4))
)

第一行包括前两个好例子,第二行包括最后一个。

Live Demo, where I played around and it passes your cases.

我的目标是可读性: 你只有3个场景,处理他们与3个独立的如果:

bool valid = false;
if (bValue1 && bValue2 && bValue3 && bValue4)
valid = true; //scenario 1
else if (bValue1 && bValue2 && bValue3 && !bValue4)
valid = true; //scenario 2
else if (bValue1 && !bValue2 && !bValue3 && !bValue4)
valid = true; //scenario 3

易于阅读和调试,恕我直言。此外,您可以分配一个变量 whichScenario,同时继续与 if

在只有3种情况下,我不会用“如果前3个值为真,我就可以避免检查第4个值”这样的话: 这会使代码更难阅读和维护。

也许吧当然不是一个优雅的解决方案,但在这种情况下是可以的: 简单易读。

如果您的逻辑变得更加复杂,抛弃这些代码,考虑使用更多的东西来存储不同的可用场景(正如 Zladeck 所建议的)。

我非常喜欢 这个答案给出的第一个建议: 易于阅读,不容易出错,可维护

(几乎)跑题了:

我在 StackOverflow 这里写的答案不多。有趣的是,上面这个被接受的答案是迄今为止我的历史上最受赞赏的答案(在我认为之前从来没有超过5-10个赞成票) ,而实际上并不是我通常认为的“正确”的方式。

But simplicity is often "the right way to do it", many people seems to think this and I should think it more than I do :)

这里真正的问题是: 当另一个开发人员(甚至作者)在几个月后必须更改这段代码时会发生什么。

我建议把这个作为比特旗来模拟:

const int SCENARIO_1 = 0x0F; // 0b1111 if using c++14
const int SCENARIO_2 = 0x0E; // 0b1110
const int SCENARIO_3 = 0x08; // 0b1000


bool bValue1 = true;
bool bValue2 = false;
bool bValue3 = false;
bool bValue4 = false;


// boolean -> int conversion is covered by standard and produces 0/1
int scenario = bValue1 << 3 | bValue2 << 2 | bValue3 << 1 | bValue4;
bool match = scenario == SCENARIO_1 || scenario == SCENARIO_2 || scenario == SCENARIO_3;
std::cout << (match ? "ok" : "error");

如果有更多方案或更多标志,则表方法比使用标志更具可读性和可扩展性。支持一个新场景只需要表中的另一行。

int scenarios[3][4] = {
{true, true, true, true},
{true, true, true, false},
{true, false, false, false},
};


int main()
{
bool bValue1 = true;
bool bValue2 = false;
bool bValue3 = true;
bool bValue4 = true;
bool match = false;


// depending on compiler, prefer std::size()/_countof instead of magic value of 4
for (int i = 0; i < 4 && !match; ++i) {
auto current = scenarios[i];
match = bValue1 == current[0] &&
bValue2 == current[1] &&
bValue3 == current[2] &&
bValue4 == current[3];
}


std::cout << (match ? "ok" : "error");
}

我们可以使用 Karnaugh map并将您的场景简化为一个逻辑方程。 I have used the 在线卡诺地图 solver with circuit for 4 variables.

enter image description here

这个产量:

enter image description here

A, B, C, D改成 bValue1, bValue2, bValue3, bValue4,这不过是:

bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4

所以你的 if陈述就变成了:

if(!(bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4))
{
// There is some error
}
  • 卡诺图是特别有用的当你有许多变量和许多条件,应该评估 true
  • After reducing the true scenarios to a logical equation, adding relevant comments indicating the true scenarios is good practice.

下面是一个简化版本:

if (bValue1 && (bValue2 == bValue3) && (bValue2 || !bValue4)) {
// acceptable
} else {
// not acceptable
}

注意,当然,这个解决方案比原来的更模糊,它的意思可能更难理解。


更新: MSalters 在评论中发现了一个更简单的表达:

if (bValue1&&(bValue2==bValue3)&&(bValue2>=bValue4)) ...

C/C + + 方式

bool scenario[3][4] = \{\{true, true, true, true},
{true, true, true, false},
{true, false, false, false}};


bool CheckScenario(bool bValue1, bool bValue2, bool bValue3, bool bValue4)
{
bool temp[] = {bValue1, bValue2, bValue3, bValue4};
for(int i = 0 ; i < sizeof(scenario) / sizeof(scenario[0]); i++)
{
if(memcmp(temp, scenario[i], sizeof(temp)) == 0)
return true;
}
return false;
}

这种方法是可伸缩的,就好像有效条件的数量在增长一样,您只需要将更多的有效条件添加到场景列表中即可。

I am denoting a, b, c, d for clarity, and A, B, C, D for complements

bValue1 = a (!A)
bValue2 = b (!B)
bValue3 = c (!C)
bValue4 = d (!D)

方程式

1 = abcd + abcD + aBCD
= a (bcd + bcD + BCD)
= a (bc + BCD)
= a (bcd + D (b ^C))

使用任何适合你的方程式。

我还想提出另一种方法。

我的想法是将布尔数转换为整数,然后使用可变模板进行比较:

unsigned bitmap_from_bools(bool b) {
return b;
}
template<typename... args>
unsigned bitmap_from_bools(bool b, args... pack) {
return (bitmap_from_bools(b) << sizeof...(pack)) | bitmap_from_bools(pack...);
}


int main() {
bool bValue1;
bool bValue2;
bool bValue3;
bool bValue4;


unsigned summary = bitmap_from_bools(bValue1, bValue2, bValue3, bValue4);


if (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u) {
//bad scenario
}
}

注意这个系统可以支持多达32个 bool 作为输入。用 unsigned long long(或 uint64_t)代替 unsigned增加了对64例的支持。 如果你不喜欢 if (summary != 0b1111u && summary != 0b1110u && summary != 0b1000u),你也可以使用另一种可变参数模板方法:

bool equals_any(unsigned target, unsigned compare) {
return target == compare;
}
template<typename... args>
bool equals_any(unsigned target, unsigned compare, args... compare_pack) {
return equals_any(target, compare) ? true : equals_any(target, compare_pack...);
}


int main() {
bool bValue1;
bool bValue2;
bool bValue3;
bool bValue4;


unsigned summary = bitmap_from_bools(bValue1, bValue2, bValue3, bValue4);


if (!equals_any(summary, 0b1111u, 0b1110u, 0b1000u)) {
//bad scenario
}
}

很容易注意到前两个场景是相似的——它们共享大部分条件。如果你想选择目前的场景,你可以这样写(这是一个修改过的 @ gian-paolo解决方案) :

bool valid = false;
if(bValue1 && bValue2 && bValue3)
{
if (bValue4)
valid = true; //scenario 1
else if (!bValue4)
valid = true; //scenario 2
}
else if (bValue1 && !bValue2 && !bValue3 && !bValue4)
valid = true; //scenario 3

更进一步,你可以注意到,第一个布尔值必须始终为真,这是一个条件,所以你可以得到:

bool valid = false;
if(bValue1)
{
if(bValue2 && bValue3)
{
if (bValue4)
valid = true; //scenario 1
else if (!bValue4)
valid = true; //scenario 2
}
else if (!bValue2 && !bValue3 && !bValue4)
valid = true; //scenario 3
}

更重要的是,您现在可以清楚地看到,bValue2和 bValue3在某种程度上是相互关联的——您可以将它们的状态提取到一些具有更合适名称的外部函数或变量(尽管这并不总是简单或合适的) :

bool valid = false;
if(bValue1)
{
bool bValue1and2 = bValue1 && bValue2;
bool notBValue1and2 = !bValue2 && !bValue3;
if(bValue1and2)
{
if (bValue4)
valid = true; //scenario 1
else if (!bValue4)
valid = true; //scenario 2
}
else if (notBValue1and2 && !bValue4)
valid = true; //scenario 3
}

这样做有一些优点和缺点:

  • 条件更小,所以更容易推理,
  • 更容易做好重命名,使这些条件更容易理解,
  • 但是,他们需要了解的范围,
  • 而且更加僵硬

If you predict that there will be changes to the above logic, you should use more straightforward approach as presented by @ gian-paolo.

否则,如果这些条件已经很好地建立,并且是永远不会改变的“坚实的规则”,请考虑我最后的代码片段。

I would also use shortcut variables for clarity. As noted earlier scenario 1 equals to scenario 2, because the value of bValue4 doesn't influence the truth of those two scenarios.

bool MAJORLY_TRUE=bValue1 && bValue2 && bValue3
bool MAJORLY_FALSE=!(bValue2 || bValue3 || bValue4)

然后你的表情就会变成:

if (MAJORLY_TRUE || (bValue1 && MAJORLY_FALSE))
{
// do something
}
else
{
// There is some error
}

给 MAJORTRUE 和 MAJORFALSE 变量(以及实际上的 bValue * vars)赋予有意义的名称将大大有助于提高可读性和维护性。

Focus on readability of the problem, not the specific "if" statement.

虽然这将产生更多的代码行,有些人可能会认为这是过分的或不必要的。我建议将您的场景从特定的布尔值中抽象出来是保持可读性的最佳方法。

通过将事物划分为可以理解的名称的类(可以随意使用函数或任何其他工具) ,我们可以更容易地显示每个场景背后的含义。更重要的是,在一个有许多可移动部件的系统中,更容易维护和加入到现有的系统中(同样,不管涉及多少额外的代码)。

#include <iostream>
#include <vector>
using namespace std;


// These values would likely not come from a single struct in real life
// Instead, they may be references to other booleans in other systems
struct Values
{
bool bValue1; // These would be given better names in reality
bool bValue2; // e.g. bDidTheCarCatchFire
bool bValue3; // and bDidTheWindshieldFallOff
bool bValue4;
};


class Scenario
{
public:
Scenario(Values& values)
: mValues(values) {}


virtual operator bool() = 0;


protected:
Values& mValues;
};


// Names as examples of things that describe your "scenarios" more effectively
class Scenario1_TheCarWasNotDamagedAtAll : public Scenario
{
public:
Scenario1_TheCarWasNotDamagedAtAll(Values& values) : Scenario(values) {}


virtual operator bool()
{
return mValues.bValue1
&& mValues.bValue2
&& mValues.bValue3
&& mValues.bValue4;
}
};


class Scenario2_TheCarBreaksDownButDidntGoOnFire : public Scenario
{
public:
Scenario2_TheCarBreaksDownButDidntGoOnFire(Values& values) : Scenario(values) {}


virtual operator bool()
{
return mValues.bValue1
&& mValues.bValue2
&& mValues.bValue3
&& !mValues.bValue4;
}
};


class Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere : public Scenario
{
public:
Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere(Values& values) : Scenario(values) {}


virtual operator bool()
{
return mValues.bValue1
&& !mValues.bValue2
&& !mValues.bValue3
&& !mValues.bValue4;
}
};


Scenario* findMatchingScenario(std::vector<Scenario*>& scenarios)
{
for(std::vector<Scenario*>::iterator it = scenarios.begin(); it != scenarios.end(); it++)
{
if (**it)
{
return *it;
}
}
return NULL;
}


int main() {
Values values = {true, true, true, true};
std::vector<Scenario*> scenarios = {
new Scenario1_TheCarWasNotDamagedAtAll(values),
new Scenario2_TheCarBreaksDownButDidntGoOnFire(values),
new Scenario3_TheCarWasCompletelyWreckedAndFireEverywhere(values)
};


Scenario* matchingScenario = findMatchingScenario(scenarios);


if(matchingScenario)
{
std::cout << matchingScenario << " was a match" << std::endl;
}
else
{
std::cout << "No match" << std::endl;
}


// your code goes here
return 0;
}

我只是在这里提供我的答案,因为在评论中有人建议展示我的解决方案。我想感谢大家的洞察力。

最后,我选择添加三个新的“场景”boolean方法:

bool CChristianLifeMinistryValidationDlg::IsFirstWeekStudentItems(CChristianLifeMinistryEntry *pEntry)
{
return (INCLUDE_ITEM1(pEntry) &&
!INCLUDE_ITEM2(pEntry) &&
!INCLUDE_ITEM3(pEntry) &&
!INCLUDE_ITEM4(pEntry));
}


bool CChristianLifeMinistryValidationDlg::IsSecondWeekStudentItems(CChristianLifeMinistryEntry *pEntry)
{
return (INCLUDE_ITEM1(pEntry) &&
INCLUDE_ITEM2(pEntry) &&
INCLUDE_ITEM3(pEntry) &&
INCLUDE_ITEM4(pEntry));
}


bool CChristianLifeMinistryValidationDlg::IsOtherWeekStudentItems(CChristianLifeMinistryEntry *pEntry)
{
return (INCLUDE_ITEM1(pEntry) &&
INCLUDE_ITEM2(pEntry) &&
INCLUDE_ITEM3(pEntry) &&
!INCLUDE_ITEM4(pEntry));
}

然后我可以像这样应用我的验证程序:

if (!IsFirstWeekStudentItems(pEntry) && !IsSecondWeekStudentItems(pEntry) && !IsOtherWeekStudentItems(pEntry))
{
; Error
}

在我的现场应用程序中,4个 bool 值实际上是从 DWORD中提取出来的,DWORD中有4个值被编码进去。

Thanks again everyone.

My previous answer is already the accepted answer, I add something here that I think is both readable, easy and in this case open to future modifications:

从@ZdeslavVojkovic 的回答(我觉得很不错)开始,我想到了这个:

#include <iostream>
#include <set>


//using namespace std;


int GetScenarioInt(bool bValue1, bool bValue2, bool bValue3, bool bValue4)
{
return bValue1 << 3 | bValue2 << 2 | bValue3 << 1 | bValue4;
}
bool IsValidScenario(bool bValue1, bool bValue2, bool bValue3, bool bValue4)
{
std::set<int> validScenarios;
validScenarios.insert(GetScenarioInt(true, true, true, true));
validScenarios.insert(GetScenarioInt(true, true, true, false));
validScenarios.insert(GetScenarioInt(true, false, false, false));


int currentScenario = GetScenarioInt(bValue1, bValue2, bValue3, bValue4);


return validScenarios.find(currentScenario) != validScenarios.end();
}


int main()
{
std::cout << IsValidScenario(true, true, true, false) << "\n"; // expected = true;
std::cout << IsValidScenario(true, true, false, false) << "\n"; // expected = false;


return 0;
}

工作时见

好吧,这就是我通常所追求的“优雅且可维护”(IMHO)解决方案,但实际上,对于 OP 来说,我之前的“一堆如果”答案更适合 OP 需求,即使它不优雅也不可维护。

这取决于它们代表什么。

例如,如果 1是一把钥匙,而 23是两个必须同意的人(除非他们同意 NOT,他们需要第三个人-4-来确认) ,那么最可读的可能是:

1 &&
(
(2 && 3)
||
((!2 && !3) && !4)
)

应民众要求:

Key &&
(
(Alice && Bob)
||
((!Alice && !Bob) && !Charlie)
)

与@GianPaolo 的精彩回答略有不同,有些人可能会觉得更容易理解:

bool any_of_three_scenarios(bool v1, bool v2, bool v3, bool v4)
{
return (v1 &&  v2 &&  v3 &&  v4)  // scenario 1
|| (v1 &&  v2 &&  v3 && !v4)  // scenario 2
|| (v1 && !v2 && !v3 && !v4); // scenario 3
}


if (any_of_three_scenarios(bValue1,bValue2,bValue3,bValue4))
{
// ...
}

我没有看到任何答案说要命名的情况,尽管 OP 的解决方案正是这样做的。

对我来说,最好将每个场景的注释封装到一个变量名或函数名中。你更有可能忽略一个评论而不是一个名字,如果将来你的逻辑发生变化,你更有可能改变一个名字而不是一个评论。无法重构注释。

If you plan on reusing these scenarios outside of your function (or might want to), then make a function that says what it evaluates (constexpr/noexcept optional but recommended):

constexpr bool IsScenario1(bool b1, bool b2, bool b3, bool b4) noexcept
{ return b1 && b2 && b3 && b4; }


constexpr bool IsScenario2(bool b1, bool b2, bool b3, bool b4) noexcept
{ return b1 && b2 && b3 && !b4; }


constexpr bool IsScenario3(bool b1, bool b2, bool b3, bool b4) noexcept
{ return b1 && !b2 && !b3 && !b4; }

如果可能,使用这些类方法(如 OP 的解决方案)。如果你不认为你会重用这个逻辑,你可以在你的函数中使用变量:

const auto is_scenario_1 = bValue1 && bValue2 && bValue3 && bValue4;
const auto is_scenario_2 = bvalue1 && bvalue2 && bValue3 && !bValue4;
const auto is_scenario_3 = bValue1 && !bValue2 && !bValue3 && !bValue4;

编译器很可能会排序,如果 bValue1为 false,那么所有场景都为 false。不要担心让它快,只是正确的和可读的。如果您分析您的代码,并发现这是一个瓶颈,因为编译器生成次优代码在 -O2或更高,然后尝试重写它。

每一个答案都过于复杂和难以阅读。对此的最佳解决方案是 switch()语句。它既具有可读性,又使添加/修改其他用例变得简单。编译器也擅长优化 switch()语句。

switch( (bValue4 << 3) | (bValue3 << 2) | (bValue2 << 1) | (bValue1) )
{
case 0b1111:
// scenario 1
break;


case 0b0111:
// scenario 2
break;


case 0b0001:
// scenario 3
break;


default:
// fault condition
break;
}

You can of course use constants and OR them together in the case statements for even greater readability.

I would aim for simplicity and readability.

bool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
bool scenario2 = bValue1 && bValue2 && bValue3 && !bValue4;
bool scenario3 = bValue1 && !bValue2 && !bValue3 && !bValue4;


if (scenario1 || scenario2 || scenario3) {
// Do whatever.
}

请确保用描述性内容替换场景名称以及标志名称。如果这对你的具体问题有意义的话,你可以考虑这个替代方案:

bool scenario1or2 = bValue1 && bValue2 && bValue3;
bool scenario3 = bValue1 && !bValue2 && !bValue3 && !bValue4;


if (scenario1or2 || scenario3) {
// Do whatever.
}

这里重要的不是谓词逻辑。它描述了你的领域,清楚地表达了你的意图。这里的关键是给所有输入和中间变量起好的名字。如果您找不到好的变量名,这可能表明您用错误的方式描述了问题。

使用 位场:

unoin {
struct {
bool b1: 1;
bool b2: 1;
bool b3: 1;
bool b4: 1;
} b;
int i;
} u;


// set:
u.b.b1=true;
...


// test
if (u.i == 0x0f) {...}
if (u.i == 0x0e) {...}
if (u.i == 0x08) {...}

PS :

That's a big pity to CPPers'. But, UB is not my worry, check it at http://coliru.stacked-crooked.com/a/2b556abfc28574a1.

首先,假设您只能修改场景检查,我将关注可读性,并将检查包装在一个函数中,这样您就可以调用 if(ScenarioA())


现在,假设您确实想要/需要优化它,我建议将紧密链接的布尔值转换为常量整数,并对它们使用位运算符

public class Options {
public const bool A = 2; // 0001
public const bool B = 4; // 0010
public const bool C = 16;// 0100
public const bool D = 32;// 1000
//public const bool N = 2^n; (up to n=32)
}


...


public isScenario3(int options) {
int s3 = Options.A | Options.B | Options.C;
// for true if only s3 options are set
return options == s3;
// for true if s3 options are set
// return options & s3 == s3
}

这使得表达场景就像列出场景的组成部分一样容易,允许您使用 switch 语句跳转到正确的条件,并且使以前没有见过这种情况的开发人员感到困惑。(C # RegexOptions 使用此模式设置标志,我不知道是否有一个 c + + 库示例)

考虑尽可能直接地将表转换为程序。根据表驱动程序,而不是用逻辑模仿它。

template<class T0>
auto is_any_of( T0 const& t0, std::initializer_list<T0> il ) {
for (auto&& x:il)
if (x==t0) return true;
return false;
}

现在

if (is_any_of(
std::make_tuple(bValue1, bValue2, bValue3, bValue4),
{
{true, true, true, true},
{true, true, true, false},
{true, false, false, false}
}
))

尽可能直接将您的真值表编码到编译器中。

实例

你也可以直接使用 std::any_of:

using entry = std::array<bool, 4>;
constexpr entry acceptable[] =
{
{true, true, true, true},
{true, true, true, false},
{true, false, false, false}
};
if (std::any_of( begin(acceptable), end(acceptable), [&](auto&&x){
return entry{bValue1, bValue2, bValue3, bValue4} == x;
}) {
}

编译器可以内联代码,消除任何迭代,并为您构建自己的逻辑。同时,您的代码准确地反映了您对问题的设想。

If (!bValue1 || (bValue2 != bValue3) || (!bValue4 && bValue2))
{
// you have a problem
}
  • B1必须始终为真
  • B2必须始终等于 b3
  • B4不可能是假的 如果 b2(和 b3)为真

很简单

做位操作看起来很干净,也很容易理解。

int bitwise = (bValue4 << 3) | (bValue3 << 2) | (bValue2 << 1) | (bValue1);
if (bitwise == 0b1111 || bitwise == 0b0111 || bitwise == 0b0001)
{
//satisfying condition
}

嵌套的 if对某些人来说可能更容易阅读

bool check(int bValue1, int bValue2, int bValue3, int bValue4)
{
if (bValue1)
{
if (bValue2)
{
// scenario 1-2
return bValue3;
}
else
{
// scenario 3
return !bValue3 && !bValue4;
}
}


return false;
}

My 2 cents: declare a variable sum (integer) so that

if(bValue1)
{
sum=sum+1;
}
if(bValue2)
{
sum=sum+2;
}
if(bValue3)
{
sum=sum+4;
}
if(bValue4)
{
sum=sum+8;
}

根据您需要的条件检查 sum,就是这样。

通过这种方式,您可以在将来轻松地添加更多的条件,从而使它非常容易阅读。

如果您去掉了布尔标志,就不必担心布尔标志的无效组合。

可接受的数值如下:

         Scenario 1 | Scenario 2 | Scenario 3
bValue1: true       | true       | true
bValue2: true       | true       | false
bValue3: true       | true       | false
bValue4: true       | false      | false

你显然有三种状态(场景)。最好将这些状态的布尔属性建模为 推导,而不是反过来。

enum State
{
scenario1,
scenario2,
scenario3,
};


inline bool isValue1(State s)
{
// (Well, this is kind of silly.  Do you really need this flag?)
return true;
}


inline bool isValue2(State s)
{
switch (s)
{
case scenario1:
case scenario2:
return true;
case scenario3:
return false;
}
}


inline bool isValue3(State s)
{
// (This is silly too.  Do you really need this flag?)
return isValue2(s);
}


inline bool isValue4(State s)
{
switch (s)
{
case scenario1:
return true;
case scenario2:
case scenario3:
return false;
}
}

这肯定比 Gian Paolo 的回答中的代码多,但是根据您的情况,这可能更容易维护:

  • 如果添加了其他布尔属性或场景,则需要修改一组中心函数。
    • Adding properties requires adding only a single function.
    • 如果添加一个场景,在 switch语句中启用关于未处理的 enum情况的编译器警告将捕获不处理该场景的属性获取器。
  • 如果需要动态修改布尔属性,则不需要在任何地方重新验证它们的组合。与其切换单个布尔标志(这可能导致无效的标志组合) ,不如使用一个状态机从一个场景转换到另一个场景。

这种方法还具有非常高效的副作用。

只是个人偏好而不是公认的答案,但我会写:

bool valid = false;
// scenario 1
valid = valid || (bValue1 && bValue2 && bValue3 && bValue4);
// scenario 2
valid = valid || (bValue1 && bValue2 && bValue3 && !bValue4);
// scenario 3
valid = valid || (bValue1 && !bValue2 && !bValue3 && !bValue4);

Several correct answers have been given to this question, but I would take a different view: 如果代码看起来太复杂,那么就有些不对劲. The code will be difficult to debug and more likely to be "one-use-only".

In real life, when we find a situation like this:

         Scenario 1 | Scenario 2 | Scenario 3
bValue1: true       | true       | true
bValue2: true       | true       | false
bValue3: true       | true       | false
bValue4: true       | false      | false

当四个状态被如此精确的模式连接时,我们正在处理模型中一些“实体”的配置

An extreme metaphor is how we would describe a "human beings" in a model, if we were not aware of their existence as unitary entities with components connected into specific degrees of freedom: we would have to describe independent states of of "torsoes", "arms", "legs" and "head" which would make it complicated to make sense of the system described. 一个直接的结果就是非常复杂的布尔表达式。

显然,降低复杂性的方法是抽象,而 c + + 中的首选工具是 对象范例

所以问题是: 为什么有这样的模式吗? 这是什么? 它代表什么?

因为我们不知道答案,所以我们可以回到数学抽象: 数组: 我们有三个场景,每个场景现在都是一个数组。

                0   1   2   3
Scenario 1:     T   T   T   T
Scenario 2:     T   T   T   F
Scenario 3:     T   F   F   F

在这一点上,你有你的初始配置。作为一个数组。例如,std::array有一个相等运算符:

这时你的语法就变成了:

if( myarray == scenario1 ) {
// arrays contents are the same


}
else if ( myarray == scenario2 ) {
// arrays contents are the same


}


else if ( myarray == scenario3 ) {
// arrays contents are the same


}
else {
// not the same


}

正如 Gian Paolo 的答案一样,它简短、清晰、易于验证/调试。在本例中,我们已经将布尔表达式的细节委托给编译器。

当你只有3个案例,而且每个案例的逻辑都很简单时,接受的答案是好的。

但是如果每个案例的逻辑更复杂,或者有更多的案例,一个更好的选择是使用 chain-of-responsibility设计模式。

创建一个 BaseValidator,其中包含对 BaseValidator的引用和对 validate的方法,以及对引用的验证程序调用验证的方法。

class BaseValidator {
BaseValidator* nextValidator;


public:
BaseValidator() {
nextValidator = 0;
}


void link(BaseValidator validator) {
if (nextValidator) {
nextValidator->link(validator);
} else {
nextValidator = validator;
}
}


bool callLinkedValidator(bool v1, bool v2, bool v3, bool v4) {
if (nextValidator) {
return nextValidator->validate(v1, v2, v3, v4);
}


return false;
}


virtual bool validate(bool v1, bool v2, bool v3, bool v4) {
return false;
}
}

然后创建许多从 BaseValidator继承的子类,用每个验证器所需的逻辑重写 validate方法。

class Validator1: public BaseValidator {
public:
bool validate(bool v1, bool v2, bool v3, bool v4) {
if (v1 && v2 && v3 && v4) {
return true;
}


return nextValidator->callLinkedValidator(v1, v2, v3, v4);
}
}

然后使用它很简单,实例化每个验证器,并将它们设置为其他验证器的根:

Validator1 firstValidator = new Validator1();
Validator2 secondValidator = new Validator2();
Validator3 thirdValidator = new Validator3();
firstValidator.link(secondValidator);
firstValidator.link(thirdValidator);
if (firstValidator.validate(value1, value2, value3, value4)) { ... }

实际上,每个验证用例都有自己的类,该类负责(a)确定验证是否匹配 that用例,以及(b)如果不匹配,则将验证发送给链中的其他用例。

请注意,我不熟悉 C + + 。我已经尝试匹配我在网上找到的一些例子的语法,但是如果这不起作用,就把它看作伪代码。下面我还有一个完整的工作 Python 示例,如果喜欢的话,可以作为基础使用。

class BaseValidator:
def __init__(self):
self.nextValidator = 0


def link(self, validator):
if (self.nextValidator):
self.nextValidator.link(validator)
else:
self.nextValidator = validator


def callLinkedValidator(self, v1, v2, v3, v4):
if (self.nextValidator):
return self.nextValidator.validate(v1, v2, v3, v4)


return False


def validate(self, v1, v2, v3, v4):
return False


class Validator1(BaseValidator):
def validate(self, v1, v2, v3, v4):
if (v1 and v2 and v3 and v4):
return True
return self.callLinkedValidator(v1, v2, v3, v4)


class Validator2(BaseValidator):
def validate(self, v1, v2, v3, v4):
if (v1 and v2 and v3 and not v4):
return True
return self.callLinkedValidator(v1, v2, v3, v4)


class Validator3(BaseValidator):
def validate(self, v1, v2, v3, v4):
if (v1 and not v2 and not v3 and not v4):
return True
return self.callLinkedValidator(v1, v2, v3, v4)


firstValidator = Validator1()
secondValidator = Validator2()
thirdValidator = Validator3()
firstValidator.link(secondValidator)
firstValidator.link(thirdValidator)
print(firstValidator.validate(False, False, True, False))

同样,您可能会发现这对于您的特定示例来说有些过度,但是如果您最终需要满足一组更复杂的情况,那么它将创建更清晰的代码。

if(!bValue1)
return false;
if(bValue2 != bValue3)
return false;
if(bValue3 == false && bValuer4 == true)
return false;
return true;