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

예를 들어 다음 시나리오는 허용되지 않습니다.

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
}

문 논리를 개선 / 단순화 할 수 있습니까?



답변

나는 가독성을 목표로 할 것입니다 : 당신은 단지 3 개의 시나리오를 가지고 있고, 3 개의 개별적인 if로 그들을 처리합니다 :

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

읽고 디버그하기 쉬운 IMHO. 또한 whichScenario진행하는 동안 변수 를 할당 할 수 있습니다 .if .

3 개의 시나리오만으로는 “처음 3 개의 값이 참이면 네 번째 값을 확인하는 것을 피할 수 있습니다.”와 같은 것을 사용하지 않을 것입니다. 그러면 코드를 읽고 유지하기가 더 어려워집니다.

우아한 솔루션이 아닙니다. 아마도 하지만이 경우에는 괜찮습니다. 쉽고 읽기 쉽습니다.

논리가 더 복잡해지면 해당 코드를 버리고 사용 가능한 다른 시나리오를 저장하기 위해 더 많은 것을 사용하는 것이 좋습니다 (Zladeck이 제안한대로).

나는 이 답변에 주어진 첫 번째 제안을 정말 좋아합니다 : 읽기 쉽고 오류가 발생하지 않으며 유지 보수가 가능합니다.

(거의) 주제에서 벗어남 :

여기 StackOverflow에서 많은 답변을 쓰지 않습니다. 위의 승인 된 답변이 내 역사상 가장 감사 한 답변 (내가 생각하기 전에 5 ~ 10 개 이상의 찬성 투표를 한 적이 없음)이지만 실제로는 일반적으로 “올바른”방법이라고 생각하는 것이 아니라는 것이 정말 재밌습니다.

그러나 단순함은 종종 “올바른 방법”입니다. 많은 사람들이 이것을 생각하는 것 같고 저보다 더 많이 생각해야합니다. 🙂


답변

단순성과 가독성을 목표로합니다.

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

여기서 중요한 것은 술어 논리가 아닙니다. 귀하의 도메인을 설명하고 귀하의 의도를 명확하게 표현합니다. 여기서 핵심은 모든 입력과 중간 변수에 좋은 이름을 지정하는 것입니다. 좋은 변수 이름을 찾을 수 없다면 문제를 잘못된 방식으로 설명하고 있다는 신호일 수 있습니다.


답변

우리는 사용할 수 있습니다 카노 맵을 논리적 식으로 시나리오를 줄일 수 있습니다. 나는 4 개의 변수에 대한 회로와 함께 온라인 Karnaugh 맵 솔버를 사용했습니다 .

결과 :

로 변경 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
}
  • Karnaugh 맵은 평가해야하는 변수와 조건이 많을 때 특히 유용합니다. true .
  • true시나리오를 논리 방정식으로 줄인 후 시나리오를 나타내는 관련 설명을 추가하는 true것이 좋습니다.

답변

여기서 진짜 질문은 다른 개발자 (또는 작성자)가 몇 달 후에이 코드를 변경해야 할 때 어떻게되는지입니다.

이것을 비트 플래그로 모델링하는 것이 좋습니다.

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

답변

내 이전 답변은 이미 받아 들여진 답변입니다. 여기에 읽기 쉽고 쉬우 며이 경우 향후 수정이 가능하다고 생각하는 내용을 추가합니다.

@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 케이스의 경우 이전의 “bunch of ifs”답변이 우아하거나 유지 관리 할 수없는 경우에도 OP 요구 사항에 더 적합합니다.


답변

다른 접근 방식도 제출하고 싶습니다.

내 생각은 부울을 정수로 변환 한 다음 가변 템플릿을 사용하여 비교하는 것입니다.

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하여 unsigned long long(또는 uint64_t64) 건으로 지원을 증가시킨다. 를 좋아하지 않는다면 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
    }
}

답변

다음은 단순화 된 버전입니다.

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

물론이 솔루션은 원래 솔루션보다 더 난독 화되어 그 의미를 이해하기 어려울 수 있습니다.


업데이트 : 주석의 MSalters는 더 간단한 표현을 찾았습니다.

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