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कथन के बजाय एक तालिका का उपयोग करूंगा । इसके अतिरिक्त, जैसा कि ये बूलियन झंडे हैं, आप प्रत्येक परिदृश्य को एक स्थिर के रूप में मॉडल कर सकते हैं और इसके खिलाफ जांच कर सकते हैं।
ज़ेडस्लाव वोजकोविक

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

14
वास्तव में परिदृश्य क्या हैं? अक्सर चीजें बहुत आसान हो जाती हैं यदि आप सामान को उचित नाम देते हैं, जैसेbool scenario1 = bValue1 && bValue2 && bValue3 && bValue4;
idclev 463035818

6
सार्थक नामों का उपयोग करते हुए, आप प्रत्येक जटिल स्थिति को एक विधि में निकाल सकते हैं और यदि उस स्थिति में उस विधि को कॉल कर सकते हैं। यह बहुत अधिक पठनीय और रखरखाव योग्य होगा। उदाहरण लिंक में दिए गए उदाहरण पर एक नज़र डालें। refactoring.guru/decompose-conditional
हार्दिक मोधा

जवाबों:


195

मैं पठनीयता के लिए लक्ष्य बनाऊंगा: आपके पास सिर्फ 3 परिदृश्य हैं, उनके साथ 3 अलग-अलग इफ्स के साथ व्यवहार करें:

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 मूल्य सही हैं तो मैं आगे के मूल्य की जांच करने से बच सकता हूं": यह आपके कोड को पढ़ने और बनाए रखने के लिए कठिन बनाने जा रहा है।

सुरुचिपूर्ण समाधान नहीं शायद निश्चित रूप से, लेकिन इस मामले में ठीक है: आसान और पठनीय।

यदि आपका तर्क अधिक जटिल हो जाता है, तो उस कोड को फेंक दें और विभिन्न उपलब्ध परिदृश्यों को संग्रहीत करने के लिए कुछ और का उपयोग करने पर विचार करें (जैसा कि ज़्लाडेक सुझाव दे रहा है)।

मैं वास्तव में इस उत्तर में दिए गए पहले सुझाव को पसंद करता हूं : पढ़ने में आसान, त्रुटि प्रवण नहीं, बनाए रखने योग्य

(लगभग) बंद विषय:

मैं StackOverflow में यहाँ बहुत से उत्तर नहीं लिखता। यह वास्तव में मज़ेदार है कि उपरोक्त स्वीकृत उत्तर मेरे इतिहास में अब तक का सबसे सराहनीय उत्तर है (मेरे विचार से पहले 5-10 से अधिक उत्थान नहीं हुए थे) जबकि वास्तव में ऐसा नहीं है जो मुझे लगता है कि यह करने का "सही" तरीका है।

लेकिन सादगी अक्सर "ऐसा करने का सही तरीका है", कई लोग ऐसा सोचते हैं और मुझे लगता है कि मुझे जितना करना है उससे अधिक सोचना चाहिए :)


1
निश्चित रूप से @hessamhedieh, यह केवल उपलब्ध परिदृश्य की एक छोटी संख्या के लिए ठीक है। जैसा कि मैंने कहा, अगर चीजें अधिक जटिल हो जाती हैं, तो किसी और चीज की बेहतर तलाश करें
जियान पाओलो

4
इसे अलग-अलग स्टेटमेंट ब्लॉक्स में बदलने के बजाय सभी शर्तों को इनिशियलाइज़र में स्टैक करके validऔर उनके साथ अलग करके सरल बनाया जा सकता है । मैं टिप्पणी में एक उदाहरण नहीं दे सकता, लेकिन आप इसे बहुत स्पष्ट बनाने के लिए बाईं ओर ऑपरेटरों को लंबवत संरेखित कर सकते हैं ; व्यक्तिगत स्थितियों को पहले से ही उतना ही छोटा कर दिया जाता है, जितना कि उन्हें होना चाहिए (के लिए ) इसलिए आपको किसी भी वर्ण को जोड़ने की आवश्यकता नहीं है जो पहले से ही है। ||valid||if
लेहशेंको

1
@ लुशेंको, मुझे लगता है कि कोष्ठक मिश्रण, && और || स्थितियाँ काफी त्रुटि प्रवण हैं (किसी ने अलग उत्तर में कहा कि ओपी में कोड में कोष्ठक में त्रुटि थी, शायद इसे ठीक कर लिया गया है)। उचित संरेखण मदद कर सकता है, निश्चित। लेकिन फायदा क्या है? अधिक पठनीय है? बनाए रखना आसान है? मुझे ऐसा नहीं लगता। जाहिर है बस मेरी राय। यकीन है, मैं वास्तव में कोड में ifs के बहुत से नफरत है।
जियान पाओलो

3
मैंने इसे इस तरह से लपेटा है if($bValue1)कि हमेशा सच होना चाहिए, तकनीकी रूप से कुछ मामूली प्रदर्शन सुधार की अनुमति है (हालांकि हम यहां लापरवाही के बारे में बात कर रहे हैं)।
मार्टिज़न

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 बताते हैं, और स्वीकृत उत्तर के विपरीत, यह स्व-दस्तावेजीकरण है। परिदृश्यों को एक अलग चरण में अपने नाम देने से बहुत अधिक पठनीय है।
एंड्रियास

106

हम एक करनौघ मानचित्र का उपयोग कर सकते हैं और अपने परिदृश्यों को तार्किक समीकरण में घटा सकते हैं। मैंने 4 चर के लिए सर्किट के साथ ऑनलाइन कर्णघ मानचित्र मानचित्र का उपयोग किया है ।

यहां छवि विवरण दर्ज करें

यह प्रदान करता है:

यहां छवि विवरण दर्ज करें

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परिदृश्यों अच्छी आदत है।

96
यद्यपि तकनीकी रूप से सही है, इस कोड को कुछ महीनों बाद किसी अन्य डेवलपर द्वारा संपादित करने के लिए बहुत सारी टिप्पणियों की आवश्यकता होती है।
ज़ेडस्लाव वोजकोविक

22
@ZdeslavVojkovic: मैं सिर्फ समीकरण के साथ एक टिप्पणी जोड़ूंगा। //!(ABC + AB'C'D') (By K-Map logic)। यदि डेवलपर को K- मैप्स सीखने का यह अच्छा समय होगा यदि वह उन्हें पहले से नहीं जानता है।
पीडब्लू

11
मैं इससे सहमत हूं, लेकिन IMO समस्या यह है कि यह समस्या डोमेन के लिए स्पष्ट रूप से मैप नहीं करता है, अर्थात प्रत्येक स्थिति विशिष्ट परिदृश्य के लिए कैसे बदलती है जो इसे बदलना / विस्तारित करना कठिन बनाता है। क्या होता है जब वहाँ Eऔर Fशर्तें और 4 नए परिदृश्य हैं? इस ifकथन को सही ढंग से अपडेट करने में कितना समय लगता है? यदि यह ठीक है या नहीं तो कोड की समीक्षा कैसे जांचती है? समस्या तकनीकी पक्ष के साथ नहीं बल्कि "व्यवसाय" पक्ष के साथ है।
ज़ेडस्लाव वोजकोविक

7
मुझे लगता है कि आप यह बता सकते हैं A: ABC + AB'C'D' = A(BC + B'C'D')( A(B ^ C)'(C + D')हालांकि यह 'सरलीकरण' कहलाने से सावधान रहना होगा)।
मैकीज पीचोटका

28
@PW यह टिप्पणी कोड के रूप में समझने योग्य है, और इस प्रकार थोड़ा व्यर्थ है। एक बेहतर टिप्पणी यह ​​बताएगी कि आप वास्तव में उस समीकरण के साथ कैसे आए, यानी कि बयान को टीटीटीटी, टीटीटीएफ और टीएफएफएफ के लिए ट्रिगर करना चाहिए। उस बिंदु पर आप कोड के बजाय उन तीन शर्तों को लिख सकते हैं और उन्हें स्पष्टीकरण की आवश्यकता नहीं है।
बर्नहार्ड बार्कर

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
सबसे अधिक रखरखाव योग्य नहीं है लेकिन निश्चित रूप से यदि स्थिति को सरल करता है। इसलिए बिटकॉइन के संचालन के आस-पास कुछ टिप्पणियां छोड़ना यहाँ एक पूर्ण आवश्यकता होगी।
एडम ज़हरान

6
IMO, तालिका सबसे अच्छा तरीका है क्योंकि यह अतिरिक्त परिदृश्य और झंडे के साथ बेहतर है।
Zdeslav Vojkovic

मुझे आपका पहला समाधान पसंद है, पढ़ने में आसान और संशोधन के लिए खुला। मैं 2 सुधार करूंगा: 1: उपयोग किए गए बूलियन मानों के एक स्पष्ट संकेत के साथ परिदृश्यएक्स को मान असाइन करें, उदाहरण 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 परिदृश्य के लिए शायद थोड़ा ओवरकिल के रूप में कुछ होने जा रहा है, ओपी ने अपने जवाब में मेरे द्वारा सुझाए गए त्वरित, गंदे और आसान समाधान को स्वीकार किया।
जियान पाओलो

4
यदि आप C ++ 14 या उच्चतर का उपयोग कर रहे हैं, तो मैं पहले समाधान के लिए बाइनरी शाब्दिक का उपयोग करने के बजाय सुझाव दूंगा - 0b1111, 0b1110 और 0b1000 बहुत स्पष्ट है। आप शायद मानक पुस्तकालय ( std::find?) का उपयोग करके इसे थोड़ा सरल कर सकते हैं ।
बर्नहार्ड बार्कर

2
मुझे लगता है कि पहले कोड को साफ करने के लिए यहां द्विआधारी शाब्दिक न्यूनतम आवश्यकता होगी। अपने वर्तमान स्वरूप में यह पूरी तरह से गूढ़ है। वर्णनात्मक पहचानकर्ता मदद कर सकते हैं लेकिन मुझे इस बारे में निश्चित नहीं है। वास्तव में, scenarioमूल्य का उत्पादन करने के लिए बिट ऑपरेशन मुझे अनावश्यक रूप से त्रुटि-प्रवण के रूप में हड़ताल करते हैं।
कोनराड रुडोल्फ

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) समाधान है जिसका मैं आमतौर पर उद्देश्य रखता हूं, लेकिन वास्तव में, ओपी के मामले के लिए, मेरा पिछला "गुच्छा का" उत्तर उत्तर की आवश्यकताओं को बेहतर ढंग से फिट करता है, भले ही यह सुरुचिपूर्ण या रखरखाव योग्य न हो।


आप जानते हैं कि आप हमेशा अपने पिछले उत्तर को संपादित कर सकते हैं और सुधार कर सकते हैं।
एंड्रियास

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 बूल तक का समर्थन कैसे कर सकती है। (या ) के unsignedसाथ बदलने से 64 मामलों में समर्थन बढ़ जाता है। यदि आपको पसंद नहीं है , तो आप अभी तक एक अन्य वैरेडिक टेम्प्लेट विधि का उपयोग कर सकते हैं:unsigned long longuint64_tif (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
मुझे इस दृष्टिकोण से प्यार है, मुख्य समारोह के नाम को छोड़कर: "बूल से ... क्या ?" - स्पष्ट रूप से क्यों नहीं bitmap_from_bools, या bools_to_bitmap?
कोनराड रुडोल्फ

हाँ @KonradRudolph, मैं एक बेहतर नाम के बारे में नहीं सोच सकता, सिवाय शायद bools_to_unsigned। बिटमैप एक अच्छा कीवर्ड है; संपादित।
स्टैक डैनी

मुझे लगता है कि आप चाहते हैं summary!= 0b1111u &&...a != b || a != cहमेशा सच है अगरb != c
MooseBoys

17

यहाँ एक सरलीकृत संस्करण है:

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

ध्यान दें, निश्चित रूप से, यह समाधान मूल एक की तुलना में अधिक अस्पष्ट है, इसका अर्थ समझने में कठिन हो सकता है।


अद्यतन: टिप्पणियों में MSalters एक और भी सरल अभिव्यक्ति मिली:

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

1
हां, लेकिन समझना मुश्किल है। लेकिन सुझाव के लिए धन्यवाद।
एंड्रयू ट्रकले

मैंने संकलक क्षमता की तुलना एक संदर्भ के रूप में आपके सरलीकरण के साथ अभिव्यक्ति को सरल बनाने के लिए की है: कंपाइलर एक्सप्लोरर । gcc को आपका इष्टतम संस्करण नहीं मिला लेकिन इसका समाधान अभी भी अच्छा है। क्लैंग और एमएसवीसी किसी भी बूलियन एक्सप्रेशन सरलीकरण का प्रदर्शन नहीं करते हैं।
ओलिव

1
@AndrewTruckle: ध्यान दें, यदि आपको अधिक पठनीय संस्करण की आवश्यकता है, तो कृपया ऐसा कहें। आपने "सरलीकृत" कहा है, फिर भी आप अपने मूल से भी अधिक वर्बोज़ संस्करण स्वीकार करते हैं।
गीजा

1
simpleवास्तव में एक अस्पष्ट शब्द है। कई लोग इसे इस संदर्भ में डेवलपर के लिए समझने के लिए सरल समझते हैं और कोड उत्पन्न करने के लिए संकलक के लिए नहीं, इसलिए अधिक क्रिया वास्तव में सरल हो सकती है।
जेडेस्लाव वोजकोविक

1
@IsmaelMiguel: जब एक तर्क सूत्र को शब्दों की संख्या के लिए अनुकूलित किया जाता है, तो मूल अर्थ आमतौर पर खो जाता है। लेकिन कोई भी इसके बारे में टिप्पणी कर सकता है, इसलिए यह स्पष्ट है कि यह क्या करता है। यहां तक ​​कि, स्वीकृत उत्तर के लिए, एक टिप्पणी नुकसान नहीं पहुंचाएगी।
भूजा

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

संकलक कोड को इनलाइन कर सकता है, और किसी भी पुनरावृत्ति को समाप्त कर सकता है और आपके लिए अपना तर्क बना सकता है। इस बीच, आपका कोड वास्तव में दर्शाता है कि आपने समस्या से कैसे छुटकारा पाया।


पहला संस्करण पढ़ना इतना आसान है और इतना मुख्य है, मुझे यह बहुत पसंद है। दूसरे को पढ़ना मुश्किल है, कम से कम मेरे लिए, और औसत से अधिक सी ++ कौशल स्तर की आवश्यकता है, निश्चित रूप से मेरे एक से अधिक। हर कोई कुछ नहीं लिख पाता है। बस सोमेथिन नया सीखा, धन्यवाद
जियान पाओलो

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 बूल मान वास्तव में निकाले गए हैं DWORDजिसमें से 4 मान इसमें एनकोडेड हैं।

फिर से सभी को धन्यवाद।


1
समाधान साझा करने के लिए धन्यवाद। :) यह वास्तव में जटिल से बेहतर है अगर स्थिति नरक है। हो सकता है कि आप अभी भी INCLUDE_ITEM1बेहतर तरीके से नाम आदि रख सकें और आप सभी अच्छे हों। :)
हार्दिक मोधा

1
@HardikModha खैर, तकनीकी रूप से वे "छात्र आइटम" हैं और ध्वज को इंगित करना है कि क्या उन्हें "शामिल" किया जाना है। इसलिए मुझे लगता है कि नाम, भले ही सामान्य लग रहा है, वास्तव में इस संदर्भ में सार्थक है। :)
एंड्रयू ट्रक 13

11

मैं किसी भी उत्तर को परिदृश्यों का नाम देने के लिए नहीं कह रहा हूं, हालांकि ओपी का समाधान ठीक यही करता है।

मेरे लिए यह टिप्पणी सबसे अच्छा है कि प्रत्येक परिदृश्य एक चर नाम या फ़ंक्शन नाम में क्या है। आपको नाम की तुलना में टिप्पणी को अनदेखा करने की अधिक संभावना है, और यदि भविष्य में आपके तर्क बदलते हैं तो आप टिप्पणी की तुलना में नाम बदलने की अधिक संभावना रखते हैं। आप किसी टिप्पणी को अस्वीकार नहीं कर सकते।

यदि आप अपने कार्य के बाहर इन परिदृश्यों का पुन: उपयोग करने की योजना बनाते हैं (या चाहते हो सकता है), तो एक फ़ंक्शन बनाएं जो कहता है कि यह मूल्यांकन करता है ( 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; }

यदि संभव हो तो इन कक्षा विधियों को बनाएं (जैसे ओपी के समाधान में)। आप अपने फ़ंक्शन के अंदर चर का उपयोग कर सकते हैं यदि आपको नहीं लगता कि आप तर्क का पुन: उपयोग करेंगे:

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 या उच्चतर उत्पन्न किया है तो इसे फिर से लिखने का प्रयास करें।


मुझे यह जियान पाओलो (पहले से ही अच्छा) समाधान की तुलना में थोड़ा अधिक पसंद है: यह नियंत्रण प्रवाह और एक चर के उपयोग से बचा जाता है जो ओवरराइट किया जाता है - अधिक कार्यात्मक शैली।
डिर्क हेरमैन

9

एसी / सी ++ रास्ता

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। एक कंपाइलर जो "कुछ भी गैर-शून्य सत्य है" का उपयोग करता है, इस कोड के विफल होने का कारण बनता है। ध्यान दें कि trueचाहिए परिवर्तित करने के लिए 1, यह सिर्फ करने की आवश्यकता नहीं है संग्रहीत जैसे।
MSalters

@MSalters, tnx, मुझे आपकी बात मिल गई है और मुझे पता है कि, थोड़े जैसे 2 is not equal to true but evaluates to true, मेरा कोड बल नहीं देता है int 1 = trueऔर तब तक काम करता है जब तक सभी सत्य समान अंतर मान में परिवर्तित नहीं हो जाते हैं, इसलिए यहाँ मेरा सवाल है: संकलक को अभिसरण के लिए यादृच्छिक कार्य क्यों करना चाहिए अंतर्निहित int के लिए सच है, क्या आप कृपया अधिक विस्तृत कर सकते हैं?
हेसाम हदीह

एक प्रदर्शन memcmpबूलियन की स्थिति का परीक्षण करने के लिए है नहीं सी ++ रास्ता मैं नहीं बल्कि संदेह नहीं है कि यह एक स्थापित सी रास्ता नहीं है, या तो है, और।
कोनराड रुडोल्फ

@ हेसामेदीह: आपके तर्क में समस्या "अंतर्निहित अंतर को सच में परिवर्तित करना" है। यह नहीं है कि कैसे कंपाइलर काम करते हैं,
MSalters

आपका कोड O (1) से O (n) तक जटिलता बढ़ाता है। किसी भी भाषा में जाने का तरीका नहीं - एक तरफ सी / सी ++ छोड़ दें।
माबेल

9

यह नोटिस करना आसान है कि पहले दो परिदृश्य समान हैं - वे अधिकांश शर्तों को साझा करते हैं। यदि आप इस समय किस परिदृश्य में चयन करना चाहते हैं, तो आप इसे इस तरह लिख सकते हैं (यह एक संशोधित @ जियान-पाओलो का समाधान है):

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अधिक पठनीयता के लिए बयानों में दे सकते हैं।


एक पुराने C- प्रोग्रामर होने के नाते, मैं "PackBools" मैक्रो को परिभाषित करूंगा और "स्विच (PackBools (a, b, c, d))) के लिए दोनों का उपयोग करूंगा और मामलों के लिए, उदाहरण के लिए सीधे" PackBools (सच) , सच्चा ...) "या उन्हें स्थानीय कॉन्स्टेंट के रूप में परिभाषित करें।" const अहस्ताक्षरित int1 परिदृश्य = PackBools (सच, सच); "
साइमन एफ

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 * var) को सार्थक नाम देने से पठनीयता और रखरखाव में बहुत मदद मिलेगी।


6

समस्या की पठनीयता पर ध्यान दें, न कि विशिष्ट "यदि" कथन।

हालांकि यह कोड की अधिक लाइनें उत्पन्न करेगा, और कुछ इसे ओवरकिल या अनावश्यक मान सकते हैं। मेरा सुझाव है कि विशिष्ट बूलियंस से अपने परिदृश्यों को अमूर्त करना पठनीयता बनाए रखने का सबसे अच्छा तरीका है।

वर्गों में चीजों को विभाजित करके (समझने के लिए स्वतंत्र रूप से उपयोग किए जाने वाले कार्यों, या जो भी अन्य उपकरण आप पसंद करते हैं) को समझने योग्य नामों के साथ - हम बहुत आसानी से प्रत्येक परिदृश्य के पीछे अर्थ दिखा सकते हैं। इससे भी महत्वपूर्ण बात यह है कि कई चलती भागों के साथ एक प्रणाली में - अपने मौजूदा सिस्टम में बनाए रखना और जुड़ना आसान है (फिर भी, इसके बावजूद कि अतिरिक्त कोड कितना अवांछित है)।

#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
कुछ बिंदु पर, वाचालता पठनीयता को नुकसान पहुंचाना शुरू कर देती है। मुझे लगता है कि यह बहुत दूर चला जाता है।
जॉलीजॉकर

2
@ जॉली जोकर मैं वास्तव में इस विशिष्ट स्थिति में सहमत हूं - हालांकि, ओपी ने जिस तरह से सब कुछ बेहद उदारतापूर्वक नाम दिया है, उससे मेरी आंत की भावना यह है कि उनका "वास्तविक" कोड संभवतः उनके द्वारा दिए गए उदाहरण की तुलना में बहुत अधिक जटिल है। वास्तव में, मैं बस इस विकल्प को वहाँ रखना चाहता था, क्योंकि यह है कि मैं इसे कुछ और अधिक जटिल / शामिल करने के लिए कैसे संरचना करूँगा। लेकिन आप सही हैं - ओपी विशिष्ट उदाहरण के लिए, यह अत्यधिक क्रिया है और मामले को बदतर बनाता है।

5

यह इस बात पर निर्भर करता है कि वे क्या प्रतिनिधित्व करते हैं।

उदाहरण के लिए यदि 1 एक कुंजी है, और 2 और 3 दो लोग हैं जिन्हें सहमत होना चाहिए (सिवाय अगर वे सहमत हैं कि NOTउन्हें तीसरे व्यक्ति की आवश्यकता है - 4 - पुष्टि करने के लिए) सबसे पठनीय हो सकता है:

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

लोकप्रिय अनुरोध द्वारा:

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

2
आप सही हो सकते हैं, लेकिन अपनी बात को स्पष्ट करने के लिए संख्याओं का उपयोग करते हुए अपने उत्तर से अलग हो जाते हैं। वर्णनात्मक नामों का उपयोग करने का प्रयास करें।
jxh

1
@jxh वे नंबर हैं जिनका ओपी इस्तेमाल किया जाता है। मैंने अभी हटा दिया 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

मैं स्पष्टता के लिए ए, बी, सी, डी और पूरक के लिए ए, बी, सी, डी को चिह्नित कर रहा हूं

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
}
  • बी 1 हमेशा सच होना चाहिए
  • b2 को हमेशा b3 के बराबर होना चाहिए
  • और b4 गलत नहीं हो सकता है यदि b2 (और b3) सत्य हैं

सरल


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


अब, यह मानते हुए कि आप वास्तव में चाहते हैं / इसे अनुकूलित करने की आवश्यकता है, मैं कसकर जुड़े हुए बुलियन को निरंतर पूर्णांक में परिवर्तित करने की सलाह दूंगा, और उन पर बिट ऑपरेटरों का उपयोग करूंगा।

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
}

यह परिदृश्यों को व्यक्त करना आसान बनाता है क्योंकि यह सूचीबद्ध है कि इसका क्या हिस्सा है, आपको सही स्थिति में जाने के लिए स्विच स्टेटमेंट का उपयोग करने की अनुमति देता है, और साथी डेवलपर्स को भ्रमित करता है जिन्होंने इसे पहले नहीं देखा है। (C # RegexOptions झंडे स्थापित करने के लिए इस पैटर्न का उपयोग करता है, मुझे नहीं पता कि क्या कोई c ++ लाइब्रेरी उदाहरण है)


वास्तव में मैं चार बूल मानों का उपयोग नहीं कर रहा हूं, लेकिन चार एम्बेडेड बौल के साथ एक डीडब्ल्यूओआर। अब इसे बदलने में बहुत देर हो चुकी है। लेकिन आपके सुझाव के लिए धन्यवाद।
एंड्रयू ट्रकले

2

नेस्टेड ifs कुछ लोगों के लिए पढ़ना आसान हो सकता है। यहाँ मेरा संस्करण है

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

यदि संभव हो तो व्यक्तिगत रूप से, यदि संभव हो तो बयान देने से बचना चाहिए। हालांकि यह मामला अच्छा और पठनीय है, एक बार नई संभावनाएं जुड़ जाने के बाद, घोंसले को पढ़ना बहुत कठिन हो सकता है। लेकिन अगर परिदृश्य कभी नहीं बदलते हैं, तो यह निश्चित रूप से एक अच्छा और पठनीय समाधान है।
दीनोमेयारबे

@ Dnomyar96 मैं सहमत हूँ। मैं व्यक्तिगत रूप से नेस्टेड ifs से भी बचता हूं। कभी-कभी यदि तर्क जटिल है, तो मेरे लिए तर्क को टुकड़ों में तोड़कर समझना आसान है। उदाहरण के लिए, एक बार जब आप bValue1ब्लॉक में प्रवेश करते हैं , तो आप अपनी मानसिक प्रक्रिया में एक नया ताजा पृष्ठ मान सकते हैं। मैं शर्त लगाता हूं कि समस्या के करीब पहुंचने का तरीका बहुत व्यक्तिगत या सांस्कृतिक हो सकता है।
सार्दोक

1

इस प्रश्न के कई सही उत्तर दिए गए हैं, लेकिन मैं एक अलग दृष्टिकोण लूंगा: यदि कोड बहुत जटिल लगता है, तो कुछ सही नहीं है । कोड को डिबग करना मुश्किल होगा और "एक-उपयोग-केवल" होने की अधिक संभावना होगी।

वास्तविक जीवन में, जब हम इस तरह की स्थिति पाते हैं:

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

जब चार राज्य इस तरह के एक सटीक पैटर्न से जुड़े होते हैं, तो हम अपने मॉडल में कुछ "इकाई" के विन्यास से निपट रहे हैं

एक चरम रूपक यह है कि हम एक मॉडल में "इंसानों" का वर्णन कैसे करेंगे, अगर हम उनके अस्तित्व के बारे में एकात्मक संस्थाओं के रूप में स्वतंत्रता के विशिष्ट डिग्री में जुड़े घटकों के साथ नहीं जानते थे: हमें "धड़" के स्वतंत्र राज्यों का वर्णन करना होगा; "हथियार", "पैर" और "सिर" जो वर्णित प्रणाली की समझ बनाने के लिए जटिल बना देगा। एक तत्काल परिणाम अप्राकृतिक रूप से जटिल बूलियन अभिव्यक्ति होगा।

जाहिर है, जटिलता को कम करने का तरीका अमूर्त है और सी ++ में पसंद का एक उपकरण वस्तु प्रतिमान है

तो सवाल यह है कि ऐसा पैटर्न क्यों है? यह क्या है और यह क्या दर्शाता है?

जब से हमें उत्तर नहीं पता है, हम एक गणितीय अमूर्तन पर वापस आ सकते हैं: सरणी : हमारे पास तीन परिदृश्य हैं, जिनमें से प्रत्येक अब एक सरणी है।

                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

}

जियान पाओलो के जवाब के रूप में, यह छोटा, स्पष्ट और आसानी से सत्यापन योग्य / डीबग करने योग्य है। इस मामले में, हमने संकलक को बूलियन अभिव्यक्तियों का विवरण सौंप दिया है।


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

यह जियान पाओलो के उत्तर की तुलना में निश्चित रूप से अधिक कोड है , लेकिन आपकी स्थिति के आधार पर, यह बहुत अधिक बनाए रखने योग्य हो सकता है:

  • अतिरिक्त बूलियन गुण या परिदृश्य जोड़े जाने पर संशोधित करने के लिए फ़ंक्शन का एक केंद्रीय सेट है।
    • संपत्तियों को जोड़ने के लिए केवल एक फ़ंक्शन जोड़ने की आवश्यकता होती है।
    • यदि कोई परिदृश्य जोड़ रहा है, तो बयानों में अनचाहे 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)) { ... }

संक्षेप में, प्रत्येक सत्यापन मामले का अपना वर्ग होता है जो (क) के लिए जिम्मेदार होता है यदि यह निर्धारित होता है कि सत्यापन मेल खाता है मामले से , और (b) यदि नहीं है तो श्रृंखला में किसी और को सत्यापन भेज रहा है।

कृपया ध्यान दें कि मैं C ++ से परिचित नहीं हूं। मैंने कुछ उदाहरणों से वाक्यविन्यास का मिलान करने का प्रयास किया है जो मुझे ऑनलाइन मिले, लेकिन अगर यह काम नहीं करता है, तो इसे छद्मकोड की तरह व्यवहार करें। मेरे पास नीचे काम करने वाला एक पूरा पायथन उदाहरण भी है, जिसे यदि चाहें तो आधार के रूप में इस्तेमाल किया जा सकता है।

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

एक सरल दृष्टिकोण उस उत्तर को पा रहा है जो आपको लगता है कि स्वीकार्य है।

हां = (बूलियन १ && बूलियन २ && बूलियन ३ && बूलियन ४) + + ...

यदि संभव हो तो बूलियन बीजगणित का उपयोग करके समीकरण को सरल बनाएं।

इस मामले में, स्वीकार्य 1 और 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 के लिए बहुत बड़ी दया है। ' लेकिन, यूबी मेरी चिंता नहीं है, इसे http://coliru.stacked-crooked.com/a/2b556abfc28574a1 पर देखें


2
यह निष्क्रिय संघ क्षेत्र तक पहुँचने के कारण यूबी का कारण बनता है।
होलीब्लैककैट

औपचारिक रूप से यह C ++ में UB है, आप यूनियन के एक सदस्य को सेट नहीं कर सकते हैं और दूसरे से नहीं पढ़ सकते हैं। तकनीकी रूप से यह बेहतर हो सकता है कि इंटीग्रल वैल्यू के बिट्स के लिए टेम्प्लेटेड गेटर्स \ सेटर्स को लागू किया जाए।
स्विफ्ट - शुक्रवार पाई

मुझे लगता है कि यदि कोई संघ के पते को एक में परिवर्तित करने के लिए व्यवहार को परिभाषित करेगा, तो व्यवहार को परिभाषित किया जाएगा unsigned char*, हालांकि मुझे लगता है कि जैसे कुछ का उपयोग ((((flag4 <<1) | flag3) << 1) | flag2) << 1) | flag1करना संभवतः अधिक कुशल होगा।
सुपरकैट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.