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

时间:2018-12-03 10:13:51

标签: c++ if-statement

我有四个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

因此,例如,这种情况是不可接受的:

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
}

可以改善/简化该语句的逻辑吗?

30 个答案:

答案 0 :(得分:196)

我的目标是提高可读性:您只有3种情况,请用3种独立的ifs处理它们:

data <- data.frame(answer,weights,year)

易于阅读和调试,恕我直言。另外,您可以在继续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

在只有3种情况下,我不会采用诸如“如果前三个值是正确的,我可以避免检查第四个值”之类的东西:它将使您的代码更难于阅读和维护。

当然不是一个很好的解决方案,但在这种情况下还可以:简单易懂。

如果您的逻辑变得更加复杂,请丢弃该代码,并考虑使用更多的东西来存储不同的可用方案(如Zladeck所建议的那样)。

我真的很喜欢this answer中提出的第一个建议:易于阅读,不易出错,易于维护

(几乎)关闭主题:

我在StackOverflow上没有写很多答案。有趣的是,上述接受的答案是迄今为止我历史上最受赞赏的答案(我认为从来没有超过5-10次投票),而实际上这并不是我通常认为的“正确”方法。 / p>

但是简单性通常是“正确的方法”,许多人似乎都认为这一点,我应该比我更重视它:)

答案 1 :(得分:121)

我将着眼于简单性和可读性。

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

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

答案 2 :(得分:104)

我们可以使用Karnaugh map并将您的方案简化为一个逻辑方程式。 我已经将Online Karnaugh map求解器与电路用于4个变量。

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时,卡诺地图特别有用。
  • true场景简化为逻辑方程式后,添加指示true场景的相关注释是一种好习惯。

答案 3 :(得分:58)

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

我建议将此建模为位标记:

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");
}

答案 4 :(得分:28)

我以前的答案已经是公认的答案,我在这里添加一些我认为既可读又容易的内容,在这种情况下可以进行将来的修改:

从@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;
}

在工作中here

查看

好吧,这是我通常旨在的“优雅且可维护的”(IMHO)解决方案,但实际上,对于OP案例,我以前的“ ifs组合”答案更符合OP的要求,即使它既不优雅也不可维护

答案 5 :(得分:19)

我还想提出另一种方法。

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

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个布尔值作为输入。将unsigned替换为unsigned long long(或uint64_t)可增加对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
    }
}

答案 6 :(得分:16)

这是简化版:

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

请注意,当然,此解决方案比原始解决方案更加模糊,其含义可能更难理解。


更新:MSalters在注释中找到了一个更简单的表达式:

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

答案 7 :(得分:12)

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

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}
  }
))

这直接将您的真值表编码到编译器中。

Live example

您也可以直接使用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;
}) {
}

编译器可以内联代码,消除任何迭代并为您构建自己的逻辑。同时,您的代码完全反映了您对问题的理解。

答案 8 :(得分:11)

尽管OP的解决方案确实做到了这一点,但我没有看到任何答案来命名方案。

对我来说,最好将每种情况的注释封装到变量名或函数名中。您更可能会忽略评论而不是名称,如果将来您的逻辑发生更改,您更可能会更改名称而不是评论。您无法重构评论。

如果您打算在功能之外(或可能想要)重用这些场景,则使一个函数说明其评估结果(constexpr / noexcept是可选的,但建议使用):

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或更高级别生成了次优代码,请尝试对其进行重写。

答案 9 :(得分:11)

I am only providing my answer here as in the comments someone suggested to show my solution. I want to thank everyone for their insights.

In the end I opted to add three new "scenario" boolean methods:

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));
}

Then I was able to apply those my my validation routine like this:

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

In my live application the 4 bool values are actually extracted from a DWORD which has 4 values encoded into it.

Thanks again everyone.

答案 10 :(得分:9)

很容易注意到前两种情况是相似的-它们共享大多数条件。如果您想选择当前所处的场景,则可以这样编写(这是@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

进一步,您会注意到,第一个布尔值必须始终为true,这是一个输入条件,因此您可以得出以下结论:

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
}

以这种方式进行操作有一些优点和缺点:

  • 条件较小,因此更容易推理它们,
  • 更好地重命名以使这些情况更容易理解,
  • 但是,他们需要了解范围
  • 而且它更僵硬

如果您预计上述逻辑将会发生变化,则应使用@gian-paolo提出的更简单的方法。

否则,如果这些条件已经确立,并且是永远不会改变的“可靠规则”,请考虑我的最后一个代码段。

答案 11 :(得分:9)

一种C / C ++方式

backup : function(data){
    data = JSON.stringify(data);
    console.log(data);
    var url =  "https://jsonblob.com/api/jsonBlob/43c83fba-f591-11e8-85a9-1542923be246";
    $.ajax({
        url: url,
        type: "PUT",
        data: data,
        dataType: 'json',
        error:function(xhr, status, e){
            console.log(status)
        }
    });

这种方法是可扩展的,就像有效条件的数量在增长一样,您只需将更多条件添加到方案列表即可。

答案 12 :(得分:7)

@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))
{
  // ...
}

答案 13 :(得分:7)

按照mch的建议,您可以执行以下操作:

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

第一行涵盖了前两个情况,第二行涵盖了最后一个情况。

Live Demo ,我在这里玩耍并通过了您的案件。

答案 14 :(得分:7)

每个答案都过于复杂且难以阅读。最好的解决方案是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;
}

您当然可以在case语句中使用常数或将它们一起使用,以提高可读性。

答案 15 :(得分:6)

为了清楚起见,我还将使用快捷方式变量。如前所述,方案1等于方案2,因为bValue4的值不会影响这两个方案的真实性。

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)赋予有意义的名称将大大提高可读性和维护性。

答案 16 :(得分:6)

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

While this will produce more lines of code, and some may consider it either overkill or unnecessary. I'd suggest that abstracting your scenarios from the specific booleans is the best way to maintain readability.

By splitting things into classes (feel free to just use functions, or whatever other tool you prefer) with understandable names - we can much more easily show the meanings behind each scenario. More importantly, in a system with many moving parts - it is easier to maintain and join into your existing systems (again, despite how much extra code is involed).

#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;
}

答案 17 :(得分:5)

这取决于它们代表什么。

例如,如果 1 是键,并且 2 3 是必须同意的两个人(除非他们在{{ 1}}他们需要第三人称- 4 -来确认),最易读的可能是:

NOT

根据普遍要求:

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

答案 18 :(得分:4)

按位操作看起来很干净而且可以理解。

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

答案 19 :(得分:3)

为清楚起见,我表示a,b,c,d,为补语表示A,B,C,D

A::_a

方程式

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

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

答案 20 :(得分:3)

只是个人偏爱接受的答案,但我会写:

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);

答案 21 :(得分:3)

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

简单

答案 22 :(得分:2)

首先,假设您只能修改方案检查,我将重点放在可读性上,并将检查包装在一个函数中,以便您可以调用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 ++库示例)

答案 23 :(得分:2)

嵌套的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;
}

答案 24 :(得分:1)

如果您摆脱了布尔标志,则不必担心布尔标志的无效组合。

  

可接受的值为:

         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's answer中的代码多,但是根据您的情况,这可能更易于维护:

  • 如果添加了其他布尔属性或方案,则有一组主要的函数可以修改。
    • 添加属性仅需要添加一个功能。
    • 如果添加方案,则在enum语句中启用有关未处理的switch情况的编译器警告将捕获不处理该方案的属性获取器。
  • 如果需要动态修改布尔属性,则无需在所有地方重新验证其组合。而不是切换单个布尔标志(这可能导致标志的无效组合),而是使用一种状态机,该状态机从一种情况过渡到另一种情况。

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

答案 25 :(得分:1)

已经为该问题提供了几个正确答案,但我会采取不同的看法:如果代码看起来过于复杂,则可能不太正确。该代码将难以调试,并且更有可能是“一次性使用”。

在现实生活中,当我们发现这样的情况时:

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

当四个状态以这种精确的模式连接时,我们正在处理模型中某些“实体”的配置

一个极端的隐喻是,如果我们不知道模型中的“人类”作为单一实体存在,其组成部分与特定的自由度相关联,那么我们将如何描述“人类”:我们将不得不描述“躯干”,“手臂”,“腿”和“头”,使理解所述系统变得复杂。 直接的结果就是不自然地复杂的布尔表达式。

很明显,降低复杂性的方法是抽象,而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)的回答一样,它简短,清晰且易于验证/反驳。在这种情况下,我们已经将布尔表达式的详细信息委托给了编译器。

答案 26 :(得分:0)

我的2美分:声明一个变量和(整数),以便

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

根据所需条件检查和,仅此而已。 这样,您可以在将来轻松添加更多条件,使其很容易阅读。

答案 27 :(得分:0)

只有3种情况且每种情况的逻辑都很简单时,可以接受的答案很好。

但是,如果每种情况的逻辑更为复杂,或者存在更多的情况,那么更好的选择是使用chain-of-responsibility设计模式。

您创建一个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)) { ... }

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

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))

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

请注意,我对C ++不熟悉。我已经尝试从网上找到的一些示例中匹配语法,但是如果这样做不起作用,请更像伪代码一样对待它。我下面还有一个完整的有效Python示例,可以根据需要将其用作基础。

{{1}}

同样,对于您的特定示例,您可能会发现这种过大的杀伤力,但是如果最终遇到需要解决的更为复杂的情况,它会创建更简洁的代码。

答案 28 :(得分:-2)

一种简单的方法是找到您认为可以接受的答案。

是=(boolean1 && boolean2 && boolean3 && boolean4)+ + ...

现在,如果可能的话,使用布尔代数简化方程。

就像在这种情况下,acceptable1和2合并为(boolean1 && boolean2 && boolean3)

因此,最终答案是:

(boolean1 && boolean2 && boolean3) || 
((boolean1 && !boolean2 && !boolean3 && !boolean4)

答案 29 :(得分:-3)

使用位字段

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

这对CPPers来说是一个很大的遗憾。但是,UB不用担心,请在http://coliru.stacked-crooked.com/a/2b556abfc28574a1进行检查。