네 가지 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_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
}
}
답변
다음은 단순화 된 버전입니다.
if (bValue1 && (bValue2 == bValue3) && (bValue2 || !bValue4)) {
// acceptable
} else {
// not acceptable
}
물론이 솔루션은 원래 솔루션보다 더 난독 화되어 그 의미를 이해하기 어려울 수 있습니다.
업데이트 : 주석의 MSalters는 더 간단한 표현을 찾았습니다.
if (bValue1&&(bValue2==bValue3)&&(bValue2>=bValue4)) ...