4 개의 부울 값이 일부 경우와 일치하는지 확인하는 논리를 개선하는 방법


118

네 가지 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
}

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


8
복잡한 if문 대신 테이블을 사용 합니다. 또한 이들은 부울 플래그이므로 각 시나리오를 상수로 모델링하고 이에 대해 확인할 수 있습니다.
Zdeslav Vojkovic 2018

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

14
실제로 시나리오는 무엇입니까? 예를 들어bool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
idclev 463035818

6
의미있는 이름을 사용하여 각 복합 조건을 메서드로 추출하고 if 조건에서 해당 메서드를 호출 할 수 있습니다. 훨씬 더 읽기 쉽고 유지 관리가 가능합니다. 예를 들어 링크에 제공된 예제를 살펴보십시오. refactoring.guru/decompose-conditional
Hardik Modha

답변:


195

나는 가독성을 목표로 할 것입니다 : 당신은 단지 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 개 이상의 찬성 투표를 한 적이 없음)이지만 실제로는 일반적으로 "올바른"방법이라고 생각하는 것이 아니라는 것이 정말 재밌습니다.

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


1
확실히 @hessamhedieh, 사용 가능한 시나리오가 적은 경우에만 괜찮습니다. 내가 말했듯이, 일이 더 뭔가 다른, 더 나은 모습 복잡 경우
지안 파올로

4
이는 별도의 명령문 블록 내에서 변경하는 대신 모든 조건을에 대한 이니셜 라이저에 스택 valid하고으로 분리하여 더욱 단순화 할 수 있습니다 . 주석에 예제를 넣을 수는 없지만 왼쪽을 따라 연산자를 수직으로 정렬하여 매우 명확하게 만들 수 있습니다. 개별 조건은 필요한만큼 이미 괄호로 묶여 있으므로 (for ) 이미있는 것 이상의 문자를 표현식에 추가 할 필요가 없습니다. ||valid||if
Leushenko

1
@Leushenko, 괄호, && 그리고 || 조건은 오류가 발생하기 쉽습니다 (다른 답변의 누군가가 OP의 코드에 괄호 안에 오류가 있다고 말했거나 수정되었을 수 있습니다). 적절한 정렬이 도움이 될 수 있습니다. 그러나 이점은 무엇입니까? 더 읽기? 유지하기 쉬울까요? 나는 그렇게 생각하지 않는다. 물론 제 의견입니다. 확실히, 나는 코드에 많은 if가있는 것을 정말로 싫어한다.
Gian Paolo

3
나는 if($bValue1)항상 사실이어야하므로 기술적으로 약간의 성능 향상을 허용하므로 ( 여기서는 무시할만한 양에 대해 이야기하고 있지만) 그것을 포장했을 것입니다 .
Martijn

2
FWIW : 시나리오는 2 개뿐입니다. 처음 2 개는 동일한 시나리오이며 의존하지 않습니다.bValue4
Dancrumb

123

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

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

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


3
+1 이것도 제가했을 것입니다. @RedFilter가 지적한 것처럼 수용된 답변과 달리 이것은 자체 문서화입니다. 별도의 단계에서 시나리오에 고유 한 이름을 지정하는 것이 훨씬 더 읽기 쉽습니다.
Andreas

106

우리는 사용할 수 있습니다 카노 맵을 논리적 식으로 시나리오를 줄일 수 있습니다. 나는 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것이 좋습니다.

96
기술적으로는 정확하지만이 코드는 몇 달 후 다른 개발자가 편집하려면 많은 주석이 필요합니다.
Zdeslav Vojkovic 18.12.03

22
@ZdeslavVojkovic : 방정식과 함께 주석을 추가합니다. //!(ABC + AB'C'D') (By K-Map logic). 개발자가 K-Maps를 아직 모르면 배우기에 좋은 시간입니다.
PW

11
나는 그것에 동의하지만 IMO의 문제는 문제 영역에 명확하게 매핑되지 않는다는 것입니다. 즉, 각 조건이 변경 / 확장을 어렵게 만드는 특정 시나리오에 매핑되는 방식입니다. EF조건과 4 개의 새로운 시나리오 가 있으면 어떻게 되나요? 이 if문을 올바르게 업데이트하는 데 얼마나 걸립 니까? 코드 검토는 정상인지 여부를 어떻게 확인합니까? 문제는 기술적 측면이 아니라 "비즈니스"측면에 있습니다.
Zdeslav Vojkovic

7
나는 당신이 고려할 수 있다고 생각합니다 A: ABC + AB'C'D' = A(BC + B'C'D')(이것은 A(B ^ C)'(C + D')내가 이것을 '단순화'라고 부르는 데 조심할지라도 고려할 수 있습니다 ).
Maciej Piechotka

28
@PW 그 주석은 코드만큼 이해할 수있는 것처럼 보이므로 약간 무의미합니다. 더 나은 의견은 실제로 그 방정식을 어떻게 생각해 냈는지 설명 할 것입니다. 즉, 문이 TTTT, TTTF 및 TFFF에 대해 트리거되어야한다는 것입니다. 그 시점에서 코드에이 세 가지 조건을 작성하고 설명이 전혀 필요하지 않을 수도 있습니다.
Bernhard Barker

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
유지 관리가 가장 쉬운 것은 아니지만 if 조건을 확실히 단순화합니다. 따라서 비트 연산에 대한 몇 가지 주석을 남기는 것은 여기서 절대적으로 필요합니다.
Adam Zahran

6
IMO, 테이블은 추가 시나리오 및 플래그로 더 잘 확장되므로 최상의 접근 방식입니다.
Zdeslav Vojkovic

읽기 쉽고 수정할 수있는 첫 번째 솔루션이 마음에 듭니다. 나는 두 가지 개선을 할 것입니다. 1 : 사용 된 부울 값을 명시 적으로 표시하여 시나리오 X에 값을 할당합니다 (예 : SCENARIO_2 = true << 3 | true << 2 | true << 1 | false;2 : SCENARIO_X 변수를 피한 다음 사용 가능한 모든 시나리오를 <std::set<int>. 시나리오를 추가하는 것은 mySet.insert( true << 3 | false << 2 | true << 1 | false;단지 3 가지 시나리오에 대해 약간의 과잉 일이 될 것입니다 .OP는 내 대답에서 제안한 빠르고 더럽고 쉬운 솔루션을 수락했습니다.
Gian Paolo

4
C ++ 14 이상을 사용하는 경우 첫 번째 솔루션에 이진 리터럴을 대신 사용하는 것이 좋습니다. 0b1111, 0b1110 및 0b1000이 훨씬 더 명확합니다. 표준 라이브러리 ( std::find?)를 사용하여 이것을 약간 단순화 할 수도 있습니다 .
Bernhard Barker

2
여기서 바이너리 리터럴 은 첫 번째 코드를 깨끗하게 만들기 위한 최소한의 요구 사항 이라는 것을 알았습니다 . 현재 형태로는 완전히 비밀 스럽습니다. 설명 식별자가 도움이 될 수 있지만 그것에 대해 확신하지 못합니다. 사실, scenario값 을 생성하는 비트 연산 은 불필요하게 오류가 발생하기 쉽다고 생각합니다.
Konrad Rudolph

27

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

@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 요구 사항에 더 적합합니다.


언제든지 이전 답변을 편집 하고 개선 할 수 있다는 것을 알고 있습니다 .
Andreas

20

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

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

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

2
주 함수의 이름을 제외하고는이 접근 방식을 좋아합니다. "from bool… to what ?" — 명시 적으로 bitmap_from_bools, 또는 bools_to_bitmap?
Konrad Rudolph

네 @KonradRudolph, 더 나은 이름을 생각할 수 없었습니다 bools_to_unsigned. 비트 맵은 좋은 키워드입니다. 편집.
Stack Danny

나는 당신이 원하는 것 같아요 summary!= 0b1111u &&.... a != b || a != c항상 true가b != c
MooseBoys

17

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

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

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


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

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

1
예,하지만 이해하기 어렵습니다. 하지만 제안 해 주셔서 감사합니다.
Andrew Truckle 2018

표현식을 단순화하는 컴파일러 기능을 참조로 단순화 한 컴파일러 explorer를 비교했습니다 . gcc가 최적의 버전을 찾지 못했지만 솔루션은 여전히 ​​좋습니다. Clang 및 MSVC는 부울 식 단순화를 수행하지 않는 것 같습니다.
Oliv

1
@AndrewTruckle : 더 읽기 쉬운 버전이 필요하다면 그렇게 말 해주세요. "단순화"라고 말했지만 원래 버전보다 훨씬 자세한 버전을 받아들입니다.
geza

1
simple실제로 모호한 용어입니다. 많은 사람들이이 맥락에서 컴파일러가 코드를 생성하는 것이 아니라 개발자가 이해하기 더 쉽다고 이해하므로 더 자세한 정보가 실제로 더 간단 할 수 있습니다.
Zdeslav Vojkovic

1
@IsmaelMiguel : 논리 공식이 용어 수에 대해 최적화되면 원래 의미는 일반적으로 손실됩니다. 그러나 주위에 주석을 달 수 있으므로 그것이하는 일이 분명합니다. 수락 된 답변에 대해서도 댓글은 해를 끼치 지 않습니다.
geza

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

이것은 가능한 한 직접 진리표를 컴파일러로 인코딩합니다.

라이브 예 .

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

컴파일러는 코드를 인라인하고 반복을 제거하고 자체 로직을 구축 할 수 있습니다. 한편, 코드는 문제를 어떻게 인식했는지 정확히 반영합니다.


첫 번째 버전은 읽기 쉽고 유지 관리가 쉬워서 정말 마음에 듭니다. 두 번째는 적어도 나에게는 읽기가 더 어렵고 C ++ 기술 수준이 평균 이상일 수 있습니다. 모든 사람이 쓸 수있는 것은 아닙니다. 그냥 새로운, 덕분에 뭔가 배웠다
지안 파올로에게

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

그런 다음 내 유효성 검사 루틴을 다음과 같이 적용 할 수있었습니다.

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

내 라이브 응용 프로그램에서 4 개의 bool 값은 실제로 DWORD4 개의 값이 인코딩 된 a에서 추출됩니다 .

다시 한 번 감사드립니다.


1
솔루션을 공유해 주셔서 감사합니다. :) 상황이 지옥이라면 실제로 복잡한 것보다 낫습니다. 어쩌면 당신은 여전히 INCLUDE_ITEM1더 나은 방법으로 등의 이름을 지을 수 있고 당신은 모두 좋습니다. :)
Hardik Modha

1
@HardikModha 글쎄요, 그들은 기술적으로 "학생 항목"이고 플래그는 "포함"될 것인지를 나타내는 것입니다. 그래서 제 생각에 그 이름은 일반적으로 들리지만 실제로이 맥락에서 의미가 있습니다. :)
Andrew Truckle

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이 거짓이면 모든 시나리오가 거짓이라고 분류 할 것입니다. 빠르게 만드는 것에 대해 걱정하지 말고 정확하고 읽기만하면됩니다. 코드를 프로파일 링하고 컴파일러가 -O2 이상에서 차선책 코드를 생성했기 때문에 이것이 병목 현상이라고 판단되면 다시 작성해보십시오.


나는 Gian Paolo의 (이미 좋은) 솔루션보다 약간 더 좋아합니다. 제어 흐름과 덮어 쓰인 변수의 사용을 피합니다.보다 기능적인 스타일입니다.
Dirk Herrmann

9

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

이 접근 방식은 유효한 조건 수가 증가하는 것처럼 확장 가능하며 시나리오 목록에 조건을 추가하기 만하면됩니다.


그래도 이것이 틀렸다고 확신합니다. 컴파일러가에 대해 단일 이진 표현 만 사용한다고 가정합니다 true. "0이 아닌 모든 것이 참"인 컴파일러는이 코드를 실패하게합니다. 참고 true해야한다 변환을 합니다 1, 그냥 할 필요가 없습니다 저장 등.
MSalters

@MSalters, tnx, 나는 당신의 요점을 얻었고 나는 모든 true가 동일한 int 값으로 변환되는 한 2 is not equal to true but evaluates to true내 코드가 강제 int 1 = true로 작동하지 않고 작동 한다는 것을 알고 있습니다 . 그래서 여기에 내 질문이 있습니다. 왜 컴파일러가 변환시 무작위로 행동해야 하는가 기본 int에 충실합니다. 더 자세히 설명해 주시겠습니까?
hessam hedieh

memcmp부울 조건을 테스트 하기 위해 수행하는 것은 C ++ 방식 이 아니며 , 이것이 확립 된 C 방식이라고 생각합니다.
Konrad Rudolph

@hessamhedieh : 논리의 문제는 "진정한 int로 변환"하는 것입니다. 그것은 컴파일러가 작동하는 방식 이 아닙니다 .
MSalters dec.

코드는 O (1)에서 O (n)으로 복잡성을 증가시킵니다. 어떤 언어로도 갈 수있는 방법이 아닙니다. C / C ++는 따로 두십시오.
mabel

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

더 나아가서 첫 번째 부울은 항상 참이어야한다는 것을 알 수 있습니다. 이것은 입력 조건이므로 다음과 같이 끝낼 수 있습니다.

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가 제시하는보다 직접적인 접근 방식을 사용해야합니다 .

그렇지 않고 이러한 조건이 잘 확립되어 있고 변경되지 않는 일종의 "단단한 규칙"이라면 마지막 코드 조각을 고려하십시오.


7

mch가 제안한대로 다음을 수행 할 수 있습니다.

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

첫 번째 줄은 두 개의 첫 번째 좋은 사례를 다루고 두 번째 줄은 마지막 사례를 가리 킵니다.

Live Demo, 내가 놀았고 그것은 당신의 사건을 통과합니다.


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

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가독성을 높이기 위해 문 에서 상수와 OR을 함께 사용할 수 있습니다 .


오래된 C 프로그래머이기 때문에 "PackBools"매크로를 정의하고 "switch (PackBools (a, b, c, d))"와 케이스에 모두 사용합니다. 예를 들어 직접 "case PackBools (true , true ...) "또는 로컬 상수로 정의합니다 .eg"const unsigned int scenario1 = PackBools (true, true ...); "
Simon F

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)에 의미있는 이름을 지정하면 가독성과 유지 관리에 많은 도움이됩니다.


6

특정 "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;
}

5
어떤 시점에서 장황함은 가독성을 해치기 시작합니다. 너무 멀리가는 것 같아요.
JollyJoker

2
@JollyJoker 저는 실제로이 특정 상황에 동의합니다. 그러나 OP가 모든 것을 매우 일반적으로 명명 한 방식에서 내 직감은 "실제"코드가 그들이 제공 한 예제보다 훨씬 더 복잡 할 가능성이 있다는 것입니다. 정말, 저는 훨씬 더 복잡하고 관련이있는 것을 위해 그것을 구조화하는 방법이기 때문에이 대안을 거기에 넣고 싶었습니다. 그러나 당신이 옳습니다-OP의 특정 예의 경우 지나치게 장황하고 문제를 악화시킵니다.

5

그것이 무엇을 나타내는 지에 달려 있습니다.

예를 들어 1 이 키이고 23 이 동의해야하는 두 사람인 경우 (확인을 NOT위해 제 3자가 필요하다는 데 동의하는 경우 제외 -4- 확인) 가장 읽기 쉬운 것은 다음과 같습니다.

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

인기있는 요청 :

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

2
당신이 옳을 수도 있지만, 당신의 요점을 설명하기 위해 숫자를 사용하는 것은 당신의 대답을 떨어 뜨립니다. 설명이 포함 된 이름을 사용해보십시오.
jxh

1
@jxh OP가 사용하는 숫자입니다. 방금 bValue.
ispiro

@jxh 이제 더 나아지기를 바랍니다.
ispiro

4

비트 연산을 수행하는 것은 매우 깨끗하고 이해하기 쉬워 보입니다.

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

1
비트 비교 는 읽기 쉽게 보입니다. 반면에 구성은 인공적으로 보입니다.
xtofl

3

나는 명확성을 위해 a, b, c, d를 표시하고 보완을 위해 A, B, C, D를 표시합니다.

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

자신에게 맞는 방정식을 사용하십시오.


3
If (!bValue1 || (bValue2 != bValue3) || (!bValue4 && bValue2))
{
// you have a problem
}
  • b1은 항상 참이어야합니다.
  • b2는 항상 b3과 같아야합니다.
  • b2 (및 b3)가 참이면 b4는 거짓 일 수 없습니다.

단순한


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

2

첫째, 시나리오 검사 만 수정할 수 있다고 가정하면 가독성에 중점을두고 검사를 함수로 래핑하여 if(ScenarioA()).


이제 실제로 이것을 최적화하고 싶거나 필요하다고 가정하면 밀접하게 연결된 Booleans를 상수 정수로 변환하고 비트 연산자를 사용하는 것이 좋습니다.

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 ++ 라이브러리 예제가 있는지 모르겠습니다.)


실제로 나는 4 개의 bool 값을 사용하지 않고 4 개의 내장 된 BOOLS가있는 DWORD를 사용하고 있습니다. 지금 변경하기에는 너무 늦었습니다. 하지만 제안 해 주셔서 감사합니다.
Andrew Truckle 2018

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

개인적으로 가능한 경우 일반적으로 if 문을 중첩하지 않습니다. 이 경우는 훌륭하고 읽기 쉽지만 새로운 가능성이 추가되면 중첩을 읽기가 매우 어려워 질 수 있습니다. 그러나 시나리오가 변경되지 않으면 확실히 멋지고 읽기 쉬운 솔루션입니다.
Dnomyar96

@ Dnomyar96 동의합니다. 개인적으로 중첩 된 if도 피합니다. 때로는 논리가 복잡하면 논리를 여러 조각으로 나누어 이해하는 것이 더 쉽습니다. 예를 들어, 일단 bValue1블록에 들어가면 그 안의 모든 것을 정신 과정에서 새로운 페이지로 취급 할 수 있습니다. 문제에 접근하는 방법은 매우 개인적이거나 문화적인 것일 수도 있습니다.
sardok

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의 대답과 마찬가지로 짧고 명확하며 쉽게 검증 / 디버깅 할 수 있습니다. 이 경우 부울 표현식의 세부 사항을 컴파일러에 위임했습니다.


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의 답변 보다 확실히 더 많은 코드 이지만 상황에 따라 훨씬 더 유지 관리가 가능할 수 있습니다.

  • 추가 부울 속성이나 시나리오가 추가되면 수정할 수있는 중앙 함수 집합이 있습니다.
    • 속성을 추가하려면 하나의 함수 만 추가하면됩니다.
    • 시나리오를 추가하는 enum경우 switch명령문 에서 처리되지 않은 케이스에 대한 컴파일러 경고를 활성화하면 해당 시나리오를 처리 하지 않는 속성 획득자를 포착합니다.
  • 부울 속성을 동적으로 수정해야하는 경우 모든 곳에서 해당 조합의 유효성을 다시 검사 할 필요가 없습니다. 개별 부울 플래그를 토글하는 대신 (잘못된 플래그 조합을 초래할 수 있음) 대신 한 시나리오에서 다른 시나리오로 전환하는 상태 머신이 있습니다.

이 접근 방식은 매우 효율적이라는 부수적 인 이점도 있습니다.


0

내 2 센트 : 변수 합계 (정수)를 선언하여

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

원하는 조건과 비교하여 합계를 확인하십시오. 이렇게하면 나중에 더 많은 조건을 쉽게 추가 할 수 있으므로 읽기가 매우 간단합니다.


0

허용되는 대답은 3 개의 케이스 만 있고 각각의 논리가 간단한 경우에 좋습니다.

그러나 각 사례에 대한 논리가 더 복잡하거나 더 많은 사례가있는 경우 훨씬 더 나은 옵션은 책임 사슬 설계 패턴 을 사용하는 것 입니다.

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)에 대한 책임이 자신의 클래스 결정을 가지고 검증과 일치하는 경우 사례 하고 (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))

다시 말하지만, 특정 예제에 대해이 과잉을 발견 할 수 있지만 충족해야하는 훨씬 더 복잡한 케이스 세트가 끝나면 훨씬 더 깨끗한 코드가 생성됩니다.


-2

간단한 접근 방식은 수용 가능하다고 생각하는 답을 찾는 것입니다.

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

이제 가능하다면 부울 대수를 사용하여 방정식을 단순화하십시오.

이 경우와 같이 accept1과 2는 (boolean1 && boolean2 && boolean3).

따라서 최종 답변은 다음과 같습니다.

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

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

추신 :

그것은 CPPers에게 큰 유감입니다. 그러나 UB는 내 걱정이 아닙니다 . http://coliru.stacked-crooked.com/a/2b556abfc28574a1 에서 확인 하십시오 .


2
이로 인해 비활성 통합 필드에 액세스하기 때문에 UB가 발생합니다.
HolyBlackCat

공식적으로 C ++의 UB이므로 하나의 union 구성원을 설정하고 다른 구성원에서 읽을 수 없습니다. 기술적으로는 정수 값 비트에 대해 템플릿 화 된 getter \ setter를 구현하는 것이 더 나을 수 있습니다.
Swift-Friday Pie

나는 unsigned char*단순히 같은 ((((flag4 <<1) | flag3) << 1) | flag2) << 1) | flag1것을 사용하는 것이 아마도 더 효율적일 것이라고 생각하지만 노동 조합의 주소를으로 변환하면 동작이 구현 정의로 전환 될 것이라고 생각 합니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.