And*_*kle 117 c++ if-statement
我有四个bool值:
bool bValue1;
bool bValue2;
bool bValue3;
bool bValue4;
Run Code Online (Sandbox Code Playgroud)
可接受的值是:
Scenario 1 | Scenario 2 | Scenario 3
bValue1: true | true | true
bValue2: true | true | false
bValue3: true | true | false
bValue4: true | false | false
Run Code Online (Sandbox Code Playgroud)
因此,例如,这种情况是不可接受的:
bValue1: false
bValue2: true
bValue3: true
bValue4: true
Run Code Online (Sandbox Code Playgroud)
目前我已经提出这个if声明来检测不良情况:
if(((bValue4 && (!bValue3 || !bValue2 || !bValue1)) ||
((bValue3 && (!bValue2 || !bValue1)) ||
(bValue2 && !bValue1) ||
(!bValue1 && !bValue2 && !bValue3 && !bValue4))
{
// There is some error
}
Run Code Online (Sandbox Code Playgroud)
可以改进/简化该陈述逻辑吗?
Gia*_*olo 196
我的目标是可读性:你只有3个场景,用3个独立的ifs处理它们:
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
Run Code Online (Sandbox Code Playgroud)
易于阅读和调试,恕我直言.此外,您可以whichScenario在继续执行时分配变量if.
只有3个场景,我不会选择这样的"如果前三个值是真的我可以避免检查第四个值":它将使你的代码更难以阅读和维护.
不是优雅的解决方案 也许 当然,但在这种情况下还可以:简单易读.
如果您的逻辑变得更复杂,请丢弃该代码并考虑使用更多内容来存储不同的可用场景(正如Zladeck建议的那样).
我非常喜欢这个答案中给出的第一个建议:易于阅读,不易出错,易于维护
(几乎)偏离主题:
我在StackOverflow上没有写很多答案.真的很有趣的是,上面接受的答案是我历史上最受欢迎的答案(在我认为之前从未有超过5-10个赞成票),而实际上并不是我通常认为的"正确"方式.
但简单往往是"正确的方式",很多人似乎都认为这一点,我应该比我做的更多:)
And*_*ers 122
我的目标是简单和可读性.
bool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
bool scenario2 = bValue1 && bValue2 && bValue3 && !bValue4;
bool scenario3 = bValue1 && !bValue2 && !bValue3 && !bValue4;
if (scenario1 || scenario2 || scenario3) {
// Do whatever.
}
Run Code Online (Sandbox Code Playgroud)
确保使用描述性的内容替换方案的名称以及标志的名称.如果它对您的具体问题有意义,您可以考虑这个替代方案:
bool scenario1or2 = bValue1 && bValue2 && bValue3;
bool scenario3 = bValue1 && !bValue2 && !bValue3 && !bValue4;
if (scenario1or2 || scenario3) {
// Do whatever.
}
Run Code Online (Sandbox Code Playgroud)
这里重要的不是谓词逻辑.它描述了您的域名并清楚地表达了您的意图.这里的关键是给所有输入和中间变量好名字.如果找不到好的变量名,可能表示您以错误的方式描述问题.
P.W*_*P.W 104
我们可以使用卡诺图来将您的场景缩小为逻辑等式.我使用了Online Karnaugh地图求解器和4个变量的电路.
这会产生:
换A, B, C, D到bValue1, bValue2, bValue3, bValue4,这只不过是:
bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4
Run Code Online (Sandbox Code Playgroud)
所以你的if陈述变成:
if(!(bValue1 && bValue2 && bValue3 || bValue1 && !bValue2 && !bValue3 && !bValue4))
{
// There is some error
}
Run Code Online (Sandbox Code Playgroud)
true. true场景减少到逻辑等式之后,添加指示true场景的相关注释是一种良好的做法.Zde*_*vic 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");
Run Code Online (Sandbox Code Playgroud)
如果有更多场景或更多标志,则表方法比使用标志更具可读性和可扩展性.支持新方案只需要表中的另一行.
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");
}
Run Code Online (Sandbox Code Playgroud)
Gia*_*olo 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;
}
Run Code Online (Sandbox Code Playgroud)
在这里看到它
嗯,这是我通常的目标"优雅和可维护"(恕我直言)的解决方案,但实际上,对于OP案例,我之前的"一堆ifs"答案更符合OP要求,即使它不优雅也不可维护.
Sta*_*nny 20
我还想提交另一种方法.
我的想法是将bools转换为整数,然后使用可变参数模板进行比较:
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
}
}
Run Code Online (Sandbox Code Playgroud)
请注意该系统如何支持最多32个bool作为输入.替换unsignedwith 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
}
}
Run Code Online (Sandbox Code Playgroud)
gez*_*eza 17
这是一个简化版本:
if (bValue1&&(bValue2==bValue3)&&(bValue2||!bValue4)) {
// acceptable
} else {
// not acceptable
}
Run Code Online (Sandbox Code Playgroud)
当然,请注意,此解决方案比原始解决方案更加模糊,其含义可能更难理解.
更新:评论中的MSalters发现了一个更简单的表达式:
if (bValue1&&(bValue2==bValue3)&&(bValue2>=bValue4)) ...
Run Code Online (Sandbox Code Playgroud)
Yak*_*ont 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;
}
Run Code Online (Sandbox Code Playgroud)
现在
if (is_any_of(
std::make_tuple(bValue1, bValue2, bValue3, bValue4),
{
{true, true, true, true},
{true, true, true, false},
{true, false, false, false}
}
))
Run Code Online (Sandbox Code Playgroud)
这可以直接将您的真值表编码到编译器中.
实例.
您也可以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;
}) {
}
Run Code Online (Sandbox Code Playgroud)
编译器可以内联代码,消除任何迭代并为您构建自己的逻辑.同时,您的代码完全反映了您如何解决问题.
And*_*kle 11
我只在这里提供我的答案,就像有人建议显示我的解决方案的评论一样.我要感谢大家的见解.
最后,我选择添加三个新的"场景" 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));
}
Run Code Online (Sandbox Code Playgroud)
然后我能够像我这样应用那些我的验证程序:
if (!IsFirstWeekStudentItems(pEntry) && !IsSecondWeekStudentItems(pEntry) && !IsOtherWeekStudentItems(pEntry))
{
; Error
}
Run Code Online (Sandbox Code Playgroud)
在我的实时应用程序中,4个bool值实际上是从DWORD有4个编码到其中的值中提取的.
再次感谢大家.
Err*_*ous 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; }
Run Code Online (Sandbox Code Playgroud)
尽可能使这些类方法(如在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;
Run Code Online (Sandbox Code Playgroud)
编译器很可能会理解,如果bValue1为false,那么所有场景都是假的.不要担心快速,正确和可读.如果您对代码进行概要分析并发现这是一个瓶颈,因为编译器在-O2或更高的位置生成了次优代码,那么请尝试重写它.
AC/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;
}
Run Code Online (Sandbox Code Playgroud)
这种方法是可扩展的,就好像有效条件的数量增加一样,您可以轻松地将更多条件添加到方案列表中.
很容易注意到前两种情况是相似的 - 它们共享大多数条件.如果你想选择你现在的场景,你可以这样写(这是一个修改过的@ 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
Run Code Online (Sandbox Code Playgroud)
更进一步,您可以注意到,第一个布尔值必须始终为真,这是一个条目条件,因此您最终可以:
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
}
Run Code Online (Sandbox Code Playgroud)
更重要的是,你现在可以清楚地看到,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
}
Run Code Online (Sandbox Code Playgroud)
这样做有一些优点和缺点:
如果您预测上述逻辑会有变化,您应该使用@ gian-paolo提供的更简单的方法.
否则,如果这些条件已经很好地建立,并且是一种永远不会改变的"可靠规则",请考虑我的最后一个代码片段.
正如mch所建议的,你可以这样做:
if(!((bValue1 && bValue2 && bValue3) ||
(bValue1 && !bValue2 && !bValue3 && !bValue4))
)
Run Code Online (Sandbox Code Playgroud)
第一行覆盖两个第一个好案例,第二行覆盖最后一行.
Live Demo,我在哪里玩,它通过你的案件.
@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))
{
// ...
}
Run Code Online (Sandbox Code Playgroud)
每个答案都过于复杂,难以阅读.对此的最佳解决方案是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;
}
Run Code Online (Sandbox Code Playgroud)
您当然可以在case语句中使用常量和OR,以获得更高的可读性.
为清晰起见,我还会使用快捷方式变量.如前所述,方案1等于方案2,因为bValue4的值不会影响这两种方案的真实性.
bool MAJORLY_TRUE=bValue1 && bValue2 && bValue3
bool MAJORLY_FALSE=!(bValue2 || bValue3 || bValue4)
Run Code Online (Sandbox Code Playgroud)
然后你的表达beomes:
if (MAJORLY_TRUE || (bValue1 && MAJORLY_FALSE))
{
// do something
}
else
{
// There is some error
}
Run Code Online (Sandbox Code Playgroud)
为MAJORTRUE和MAJORFALSE变量(以及实际上为bValue*变量)赋予有意义的名称对于可读性和维护有很大帮助.
关注问题的可读性,而不是具体的"if"语句.
虽然这会产生更多的代码行,但有些人可能会认为它过分或不必要.我建议从特定的布尔值中抽象你的场景是保持可读性的最佳方法.
通过将事物分成类(可以随意使用函数,或者您喜欢的任何其他工具)和可理解的名称 - 我们可以更容易地显示每个场景背后的含义.更重要的是,在具有许多移动部件的系统中 - 更容易维护和加入现有系统(尽管有多少额外代码被调用).
#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;
}
Run Code Online (Sandbox Code Playgroud)
这取决于他们代表什么.
例如,如果1是一个密钥,2和3是必须同意的两个人(除非他们同意NOT他们需要第三个人 - 4 - 确认),最可读的可能是:
1 &&
(
(2 && 3)
||
((!2 && !3) && !4)
)
Run Code Online (Sandbox Code Playgroud)
按流行要求:
Key &&
(
(Alice && Bob)
||
((!Alice && !Bob) && !Charlie)
)
Run Code Online (Sandbox Code Playgroud)