कैसे "अगर" जंजीरों से बचने के लिए?


266

यह मानते हुए कि मेरे पास यह छद्म कोड है:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}

executeThisFunctionInAnyCase();

executeStepXयदि पहले और केवल सफल होने पर कार्य निष्पादित किया जाना चाहिए। किसी भी मामले में, executeThisFunctionInAnyCaseफ़ंक्शन को अंत में बुलाया जाना चाहिए। मैं प्रोग्रामिंग में एक नौसिखिया हूं, इसलिए बहुत बुनियादी सवाल के लिए खेद है: क्या एक तरह से (उदाहरण के लिए C / C ++ में) उस लंबी ifश्रृंखला से बचने के लिए उस तरह के "कोड के पिरामिड" का उत्पादन होता है, कोड की देयता की कीमत पर ?

मुझे पता है कि यदि हम executeThisFunctionInAnyCaseफ़ंक्शन कॉल को छोड़ सकते हैं, तो कोड को सरल बनाया जा सकता है:

bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;

लेकिन बाधा executeThisFunctionInAnyCaseफ़ंक्शन कॉल है। क्या breakकिसी तरह से बयान का इस्तेमाल किया जा सकता है?


254
@ Frédéric Hamidi गलत गलत गलत! कभी यह मत कहो कि अपवादों के साथ अपने कार्यक्रम प्रवाह को चलाना अच्छा है! अपवाद निश्चित रूप से इस उद्देश्य के लिए अनुकूल नहीं हैं, कई कारणों से।
पियोट ज़िएरहोफ़र

26
@ पॉट्र, मैं पायथन द्वारा खराब हो गया था (जो वास्तव में इसे प्रोत्साहित करता है)। मुझे पता है कि अपवादों का उपयोग C ++ में प्रवाह नियंत्रण के लिए नहीं किया जाना चाहिए, लेकिन क्या यह वास्तव में यहाँ प्रवाह नियंत्रण है? क्या एक समारोह में लौटने falseको असाधारण स्थिति के समान नहीं माना जा सकता है?
Frédéric Hamidi

13
जो कि किसी प्रोग्राम के शब्दार्थ पर निर्भर करता है। एक falseवापसी बहुत सामान्य हो सकती है।
सुबह

28
मैंने आपके प्रश्न को उसके पहले संशोधन में वापस ले लिया है। आपके प्रश्नों की एक निश्चित संख्या (> 0) प्राप्त होने के बाद आपको मौलिक रूप से अपना प्रश्न नहीं बदलना चाहिए, क्योंकि यह उस क्षण तक दिए गए सभी उत्तरों को अमान्य कर देगा और भ्रम पैदा करेगा। इसके बजाय एक नया प्रश्न खोलें।
जूता

13
मेरी इच्छा है कि सभी "नौसिखिया प्रोग्रामर" इस ​​तरह के डिजाइन प्रश्न पूछेंगे।
जेजेन थॉमस

जवाबों:


486

आप &&(तर्क और) का उपयोग कर सकते हैं :

if (executeStepA() && executeStepB() && executeStepC()){
    ...
}
executeThisFunctionInAnyCase();

यह आपकी दोनों आवश्यकताओं को पूरा करेगा:

  • executeStep<X>()मूल्यांकन तभी करना चाहिए जब पिछला वाला सफल हुआ (इसे शॉर्ट सर्किट मूल्यांकन कहा जाता है )
  • executeThisFunctionInAnyCase() किसी भी मामले में निष्पादित किया जाएगा

29
यह दोनों शब्दार्थिक रूप से सही है (वास्तव में, हम चाहते हैं कि सभी परिस्थितियां सच हों) और साथ ही एक बहुत अच्छी प्रोग्रामिंग तकनीक (शॉर्ट सर्किट मूल्यांकन)। इसके अलावा, इसका उपयोग किसी भी जटिल स्थिति में किया जा सकता है जहां फ़ंक्शन बनाने से कोड गड़बड़ हो जाएगा।
Sanchises

58
@RobAu: तब जूनियर प्रोग्रामर्स के लिए यह अच्छा होगा कि वे अंत में कोड देखें जो शॉर्ट-कट मूल्यांकन पर निर्भर करता है और उन्हें इस विषय को फिर से शुरू करने के लिए प्रेरित कर सकता है जो अंततः उन्हें वरिष्ठ प्रोग्रामर बनने के रास्ते में मदद करेगा। तो स्पष्ट रूप से एक जीत-जीत: सभ्य कोड और किसी ने इसे पढ़ने से कुछ सीखा।
x4u

24
यह शीर्ष उत्तर होना चाहिए
प्रदर्शन नाम

61
@RobAu: यह कुछ अस्पष्ट वाक्यविन्यास चाल का फायदा उठाने वाला हैक नहीं है, यह लगभग हर प्रोग्रामिंग भाषा में, अत्यधिक मानक अभ्यास होने के बिंदु पर अत्यधिक मुहावरेदार है।
ब्लूराजा - डैनी पफ्लुगुएफ्ट

38
यह समाधान केवल तभी लागू होता है यदि स्थितियां वास्तव में सरल फ़ंक्शन कहती हैं। वास्तविक कोड में, वे स्थितियां 2-5 लाइनें लंबी हो सकती हैं (और खुद कई अन्य का संयोजन हो सकता है ) &&और ||इसलिए कोई तरीका नहीं है कि आप ifपठनीयता को खराब किए बिना उन्हें एक बयान में शामिल करना चाहेंगे । और उन परिस्थितियों को बाहरी कार्यों में स्थानांतरित करना हमेशा आसान नहीं होता है, क्योंकि वे पहले से गणना किए गए स्थानीय चर पर बहुत अधिक निर्भर हो सकते हैं, जो कि यदि आप प्रत्येक को व्यक्तिगत तर्क के रूप में पारित करने की कोशिश करते हैं तो एक भयानक गड़बड़ पैदा करेगा।
हम्सटरजेन

358

काम करने के लिए अपना दूसरा संस्करण प्राप्त करने के लिए बस एक अतिरिक्त फ़ंक्शन का उपयोग करें:

void foo()
{
  bool conditionA = executeStepA();
  if (!conditionA) return;

  bool conditionB = executeStepB();
  if (!conditionB) return;

  bool conditionC = executeStepC();
  if (!conditionC) return;
}

void bar()
{
  foo();
  executeThisFunctionInAnyCase();
}

या तो गहराई से नेस्टेड इफ़ेक्ट्स (आपका पहला संस्करण) का उपयोग करना या "एक फ़ंक्शन के भाग" से बाहर निकलने की इच्छा का आमतौर पर मतलब है कि आपको एक अतिरिक्त फ़ंक्शन की आवश्यकता है।


51
+1 आख़िरकार किसी ने एक उत्तर दिया। यह सबसे सही, सुरक्षित और पठनीय तरीका है, मेरी राय में।
लंडिन

31
+1 यह "सिंगल रिस्पॉन्सिबिलिटी प्रिंसिपल" का अच्छा चित्रण है। fooसंबंधित परिस्थितियों और कार्यों के अनुक्रम के माध्यम से कार्य करता है। फंक्शन barको साफ तौर पर फैसलों से अलग किया जाता है। यदि हमने स्थितियों और कार्यों का विवरण देखा, तो यह पता चल सकता है कि fooअभी भी बहुत कुछ किया जा रहा है, लेकिन अभी के लिए यह एक अच्छा उपाय है।
ग्रेनाइट रीबर्ट

13
नकारात्मक पक्ष यह है कि सी में नेस्टेड फ़ंक्शंस नहीं हैं, यदि barआपके द्वारा चर का उपयोग करने के लिए आवश्यक 3 चरणों को मैन्युअल रूप से उन्हें fooमापदंडों के रूप में पारित करना होगा । अगर ऐसा है और अगर fooकेवल एक बार मुझे बुलाया जाता है तो मैं दो कसकर युग्मित कार्यों को परिभाषित करने से बचने के लिए गोटो संस्करण का उपयोग करने की दिशा में गलत करूंगा जो कि बहुत पुन: प्रयोज्य नहीं होगा।
18 अगस्त को hugomg

7
सी के लिए शॉर्ट-सर्कुलेटिंग सिंटैक्स के बारे में निश्चित नहीं है, लेकिन सी # फू () में लिखा जा सकता हैif (!executeStepA() || !executeStepB() || !executeStepC()) return
ट्रैविस

6
@ user1598390 गोटो का उपयोग हर समय किया जाता है, खासकर सिस्टम प्रोग्रामिंग में जब आपको बहुत सारे सेट-अप कोड को खोलना पड़ता है।
स्कूटी बाउर

166

पुराने स्कूल C प्रोग्रामर gotoइस मामले में उपयोग करते हैं । यह gotoवास्तव में लिनक्स स्टाइलगाइड द्वारा प्रोत्साहित किए जाने का एक उपयोग है , इसे केंद्रीकृत फ़ंक्शन निकास कहा जाता है:

int foo() {
    int result = /*some error code*/;
    if(!executeStepA()) goto cleanup;
    if(!executeStepB()) goto cleanup;
    if(!executeStepC()) goto cleanup;

    result = 0;
cleanup:
    executeThisFunctionInAnyCase();
    return result;
}

कुछ लोग gotoशरीर को एक लूप में लपेटकर और उससे टूटकर उपयोग करते हैं, लेकिन प्रभावी रूप से दोनों एक ही काम करते हैं। gotoयदि आप किसी अन्य सफाई ही अगर जरूरत दृष्टिकोण बेहतर है executeStepA()सफल था:

int foo() {
    int result = /*some error code*/;
    if(!executeStepA()) goto cleanupPart;
    if(!executeStepB()) goto cleanup;
    if(!executeStepC()) goto cleanup;

    result = 0;
cleanup:
    innerCleanup();
cleanupPart:
    executeThisFunctionInAnyCase();
    return result;
}

पाश दृष्टिकोण के साथ आप उस मामले में दो स्तरों के छोरों के साथ समाप्त हो जाएंगे।


108
+1। बहुत सारे लोग देखते हैं gotoऔर तुरंत सोचते हैं "यह भयानक कोड है" लेकिन इसके वैध उपयोग हैं।
ग्रीम पेरो

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

29
-1 मैं देख रहा हूं gotoऔर मुझे यह देखने के लिए सोचने की जरूरत नहीं है कि यह भयानक कोड है। मुझे एक बार ऐसा बनाए रखना पड़ा, यह बहुत बुरा है। ओपी सवाल के अंत में एक उचित सी भाषा विकल्प सुझाता है, और मैंने अपने उत्तर में इसे शामिल किया है।
चीयर्स एंड हीथ। - अल्फ

56
गोटो के इस सीमित, स्व-निहित उपयोग के साथ कुछ भी गलत नहीं है। लेकिन सावधान रहना, गोटो है एक प्रवेश द्वार दवा और यदि आप सावधान आप करेंगे एक दिन एहसास है कि कोई भी अपने पैर के साथ किसी और खाती स्पेगेटी, और आप इसे करने के बाद आप सोचा था कि "मैं तो बस इसे ठीक कर सकते 15 साल शुरू करने के लिए कर रहा हूँ नहीं कर रहे हैं एक लेबल के साथ अन्यथा भयानक बग ... "
टिम पोस्ट

66
मैंने इस शैली में अत्यंत स्पष्ट, आसान-से-बनाए कोड की शायद एक लाख पंक्तियाँ लिखी हैं। समझने के लिए दो मुख्य बातें हैं (1) अनुशासन! प्रत्येक फ़ंक्शन के लेआउट के लिए एक स्पष्ट दिशानिर्देश स्थापित करें और केवल अत्यधिक आवश्यकता पर इसका उल्लंघन करें, और (2) समझें कि हम यहां क्या कर रहे हैं , एक भाषा में अपवादों का अनुकरण कर रहे हैं जो उनके पास नहीं हैthrowकई मायनों में इससे भी बदतर है gotoक्योंकि throwयह स्थानीय संदर्भ से भी स्पष्ट नहीं है जहां आप समाप्त करने जा रहे हैं! गोटो-शैली नियंत्रण प्रवाह के लिए समान डिज़ाइन विचार का उपयोग करें जैसा कि आप अपवादों के लिए करेंगे।
एरिक लिपर्ट

131

यह एक सामान्य स्थिति है और इससे निपटने के कई सामान्य तरीके हैं। यहाँ एक विहित उत्तर पर मेरा प्रयास है। कृपया टिप्पणी करें कि क्या मुझे कुछ याद आया और मैं इस पोस्ट को अद्यतित रखूंगा।

यह एक तीर है

आप जिस पर चर्चा कर रहे हैं, वह तीर विरोधी पैटर्न के रूप में जाना जाता है । इसे एक तीर कहा जाता है क्योंकि नेस्टेड इफ़ की श्रृंखला कोड ब्लॉक बनाती है जो आगे और पीछे दाईं ओर फैलती है और फिर बाईं ओर वापस, एक दृश्य तीर बनाती है जो कोड संपादक फलक के दाईं ओर "अंक" बनाती है।

गार्ड के साथ तीर को समतल करें

एरो से बचने के कुछ सामान्य तरीकों पर यहां चर्चा की गई है । सबसे सामान्य तरीका एक गार्ड पैटर्न का उपयोग करना है , जिसमें कोड पहले अपवाद प्रवाह को संभालता है और फिर मूल प्रवाह को संभालता है, जैसे कि इसके बजाय

if (ok)
{
    DoSomething();
}
else
{
    _log.Error("oops");
    return;
}

... आप उपयोग करेंगे ...।

if (!ok)
{
    _log.Error("oops");
    return;
} 
DoSomething(); //notice how this is already farther to the left than the example above

जब गार्ड्स की एक लंबी श्रृंखला होती है, तो यह कोड काफी हद तक समतल हो जाता है क्योंकि सभी गार्ड बाईं ओर सभी तरह से दिखाई देते हैं और आपके इफ़ेक्ट नस्टेड नहीं होते हैं। इसके अलावा, आप अपनी संबंधित त्रुटि के साथ लॉजिक स्थिति को नेत्रहीन रूप से बाँध रहे हैं, जिससे यह बताना आसान हो जाता है कि क्या हो रहा है:

तीर:

ok = DoSomething1();
if (ok)
{
    ok = DoSomething2();
    if (ok)
    {
        ok = DoSomething3();
        if (!ok)
        {
            _log.Error("oops");  //Tip of the Arrow
            return;
        }
    }
    else
    {
       _log.Error("oops");
       return;
    }
}
else
{
    _log.Error("oops");
    return;
}

रक्षक:

ok = DoSomething1();
if (!ok)
{
    _log.Error("oops");
    return;
} 
ok = DoSomething2();
if (!ok)
{
    _log.Error("oops");
    return;
} 
ok = DoSomething3();
if (!ok)
{
    _log.Error("oops");
    return;
} 
ok = DoSomething4();
if (!ok)
{
    _log.Error("oops");
    return;
} 

यह उद्देश्यपूर्ण और मात्रात्मक रूप से पढ़ने में आसान है क्योंकि

  1. किसी दिए गए लॉजिक ब्लॉक के लिए {और} अक्षर एक साथ करीब हैं
  2. किसी विशेष पंक्ति को समझने के लिए आवश्यक मानसिक संदर्भ की मात्रा छोटी है
  3. एक शर्त के साथ जुड़े तर्क की संपूर्णता एक पृष्ठ पर होने की अधिक संभावना है
  4. पेज / आई ट्रैक को स्क्रॉल करने के लिए कोडर की आवश्यकता बहुत कम है

अंत में कॉमन कोड कैसे जोड़ें

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

  1. यह कुछ लोगों को गलत तरीके से रगड़ता है, उदाहरण के लिए पास्कल पर कोड करने वाले लोगों ने सीखा है कि एक कार्य = एक निकास बिंदु।
  2. यह कोड का एक खंड प्रदान नहीं करता है जो बाहर निकलने पर निष्पादित होता है, चाहे जो भी विषय हो।

नीचे मैंने इस सीमा के आसपास काम करने के लिए या तो भाषा सुविधाओं का उपयोग करके या पूरी तरह से समस्या से बचने के लिए कुछ विकल्प प्रदान किए हैं।

विकल्प 1. आप ऐसा नहीं कर सकते: उपयोग करें finally

दुर्भाग्य से, एक c ++ डेवलपर के रूप में, आप ऐसा नहीं कर सकते। लेकिन यह उन भाषाओं के लिए नंबर एक उत्तर है, जिनमें अंत में कीवर्ड होता है, क्योंकि यह वास्तव में यही है।

try
{
    if (!ok)
    {
        _log.Error("oops");
        return;
    } 
    DoSomething(); //notice how this is already farther to the left than the example above
}
finally
{
    DoSomethingNoMatterWhat();
}

विकल्प 2. समस्या से बचें: अपने कार्यों को पुनर्स्थापित करें

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

यहाँ एक उदाहरण है:

void OuterFunction()
{
    DoSomethingIfPossible();
    DoSomethingNoMatterWhat();
}

void DoSomethingIfPossible()
{
    if (!ok)
    {
        _log.Error("Oops");
        return;
    }
    DoSomething();
}

विकल्प 3. भाषा चाल: एक नकली लूप का उपयोग करें

एक और आम ट्रिक जो मैं देख रहा हूँ (सच) और ब्रेक का उपयोग कर रहा हूँ, जैसा कि अन्य उत्तरों में दिखाया गया है।

while(true)
{
     if (!ok) break;
     DoSomething();
     break;  //important
}
DoSomethingNoMatterWhat();

हालांकि यह प्रयोग करने की तुलना में कम "ईमानदार" है goto, फिर भी रिफैक्टिंग करते समय गड़बड़ होने की संभावना कम है, क्योंकि यह स्पष्ट रूप से तर्क की गुंजाइश की सीमाओं को चिह्नित करता है। एक भोली कोडर जो आपके लेबल को काटती है और चिपकाती है या आपके gotoकथन बड़ी समस्याएँ पैदा कर सकते हैं! (और स्पष्ट रूप से पैटर्न बहुत आम है अब मुझे लगता है कि यह स्पष्ट रूप से इरादे का संचार करता है, और इसलिए "बेईमान" बिल्कुल नहीं है)।

इस विकल्प के अन्य रूप हैं। उदाहरण के लिए, एक के switchबजाय उपयोग कर सकते हैं whilebreakकीवर्ड के साथ कोई भी भाषा निर्माण शायद काम करेगा।

विकल्प 4. वस्तु जीवन चक्र का लाभ उठाएं

एक अन्य दृष्टिकोण वस्तु जीवन चक्र का लाभ उठाता है। अपने पैरामीटर्स के चारों ओर ले जाने के लिए एक संदर्भ ऑब्जेक्ट का उपयोग करें (ऐसा कुछ जो हमारे भोले उदाहरण में संदिग्ध रूप से कमी है) और जब आप काम कर रहे हों तो इसका निपटान करें।

class MyContext
{
   ~MyContext()
   {
        DoSomethingNoMatterWhat();
   }
}

void MainMethod()
{
    MyContext myContext;
    ok = DoSomething(myContext);
    if (!ok)
    {
        _log.Error("Oops");
        return;
    }
    ok = DoSomethingElse(myContext);
    if (!ok)
    {
        _log.Error("Oops");
        return;
    }
    ok = DoSomethingMore(myContext);
    if (!ok)
    {
        _log.Error("Oops");
    }

    //DoSomethingNoMatterWhat will be called when myContext goes out of scope
}

नोट: सुनिश्चित करें कि आप अपनी पसंद की भाषा के वस्तु जीवन चक्र को समझते हैं। काम करने के लिए आपको किसी प्रकार के नियतात्मक कचरा संग्रह की आवश्यकता होती है, अर्थात आपको यह जानना होगा कि विध्वंसक कब कहा जाएगा। कुछ भाषाओं में आपको Disposeएक विध्वंसक के बजाय उपयोग करने की आवश्यकता होगी ।

विकल्प 4.1। ऑब्जेक्ट जीवन चक्र (आवरण पैटर्न) का लाभ उठाएं

यदि आप एक वस्तु-उन्मुख दृष्टिकोण का उपयोग करने जा रहे हैं, तो यह सही हो सकता है। यह विकल्प संसाधनों को "रैप" करने के लिए एक वर्ग का उपयोग करता है जिसे सफाई की आवश्यकता होती है, साथ ही साथ इसके अन्य संचालन भी।

class MyWrapper 
{
   bool DoSomething() {...};
   bool DoSomethingElse() {...}


   void ~MyWapper()
   {
        DoSomethingNoMatterWhat();
   }
}

void MainMethod()
{
    bool ok = myWrapper.DoSomething();
    if (!ok)
        _log.Error("Oops");
        return;
    }
    ok = myWrapper.DoSomethingElse();
    if (!ok)
       _log.Error("Oops");
        return;
    }
}
//DoSomethingNoMatterWhat will be called when myWrapper is destroyed

फिर से, सुनिश्चित करें कि आप अपने जीवन चक्र को समझते हैं।

विकल्प 5. भाषा की चाल: शॉर्ट-सर्किट मूल्यांकन का उपयोग करें

शॉर्ट सर्किट मूल्यांकन का लाभ उठाने के लिए एक और तकनीक है ।

if (DoSomething1() && DoSomething2() && DoSomething3())
{
    DoSomething4();
}
DoSomethingNoMatterWhat();

यह समाधान && ऑपरेटर के कार्य करने के तरीके का लाभ उठाता है। जब बाएं हाथ की ओर && झूठ का मूल्यांकन करता है, तो दाहिने हाथ की तरफ का मूल्यांकन कभी नहीं किया जाता है।

कॉम्पैक्ट कोड की आवश्यकता होने पर यह ट्रिक सबसे अधिक उपयोगी होती है और जब कोड में बहुत अधिक रखरखाव देखने की संभावना नहीं होती है, जैसे कि आप एक प्रसिद्ध एल्गोरिथ्म को लागू कर रहे हैं। अधिक सामान्य कोडिंग के लिए इस कोड की संरचना बहुत भंगुर है; यहां तक ​​कि तर्क में मामूली बदलाव भी कुल पुनर्लेखन को गति प्रदान कर सकता है।


12
आखिरकार? C ++ में अंत में क्लॉज नहीं है। एक विध्वंसक के साथ एक वस्तु का उपयोग उन स्थितियों में किया जाता है जहां आपको लगता है कि आपको अंत में क्लॉज की आवश्यकता है।
बिल डोर

1
उपरोक्त दो टिप्पणियों को समायोजित करने के लिए संपादित।
जॉन वू

2
तुच्छ कोड के साथ (उदाहरण में मेरे उदाहरण में) नेस्टेड पैटर्न संभवतः अधिक समझदार हैं। वास्तविक विश्व कोड (जो कई पृष्ठों को फैला सकता है) के साथ, गार्ड पैटर्न को पढ़ना आसान है क्योंकि इसमें कम स्क्रॉलिंग और कम आई ट्रैकिंग की आवश्यकता होगी, उदाहरण के लिए {से} की औसत दूरी कम है।
जॉन वू

1
मैंने नेस्टेड पैटर्न देखा है जहां कोड 1920 x 1080 स्क्रीन पर दिखाई नहीं दे रहा था ... यह जानने की कोशिश करें कि तीसरी कार्रवाई विफल होने पर त्रुटि से निपटने के कोड का प्रदर्शन कैसे किया जाएगा ... मैंने जबकि {{}} का उपयोग किया है (0) इसके बजाय आपको अंतिम विराम की आवश्यकता नहीं है (दूसरी ओर, जबकि (सत्य) {...} फिर से शुरू करने के लिए "जारी रखने" की अनुमति देता है।
gnasher729

2
आपका विकल्प 4 वास्तव में C ++ में एक मेमोरी लीक है (मामूली सिंटैक्स त्रुटि की अनदेखी)। कोई इस तरह के मामले में "नए" का उपयोग नहीं करता है, बस "MyContext myContext;" कहें।
सुमुदु फर्नांडो

60

बस करो

if( executeStepA() && executeStepB() && executeStepC() )
{
    // ...
}
executeThisFunctionInAnyCase();

यह इत्ना आसान है।


तीन संपादनों के कारण, जिनमें से प्रत्येक ने मौलिक रूप से प्रश्न को बदल दिया है (चार यदि कोई संशोधन # 1 संस्करण में वापस गिनता है), तो मैं उस कोड उदाहरण को शामिल करता हूं जिसका मैं उत्तर दे रहा हूं:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}

executeThisFunctionInAnyCase();

14
मैंने प्रश्न के पहले संस्करण के जवाब में (अधिक स्पष्ट रूप से) इसका जवाब दिया, और ऑर्बिट में डार्कनेस रेस द्वारा कुछ टिप्पणी और संपादन के बाद बिल छिपकली द्वारा हटाए जाने से पहले इसे 20 अपवोट मिला।
चीयर्स एंड हीथ। - अल्फ

1
@ चेरसन्ध-अल्फ: मैं विश्वास नहीं कर सकता कि यह मॉड-डिलीट था। यह बहुत बुरा है। (+1)
पैरामैग्नेटिक क्रोइसैन

2
आपके साहस को मेरा +1! (3 बार फर्क पड़ता है: डी)।
बजे

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

1
@JoeBlow: मैं इस पर अल्फ के साथ हूं - मुझे &&सूची बहुत स्पष्ट है। मैं आमतौर पर अलग-अलग लाइनों पर शर्तों को तोड़ता हूं और // explanationप्रत्येक के लिए एक अनुगामी जोड़ देता हूं .... अंत में, यह देखने के लिए बड़े पैमाने पर कम कोड है, और एक बार आप समझ जाते हैं कि कैसे &&काम करता है कोई निरंतर मानसिक प्रयास नहीं है। मेरी धारणा है कि अधिकांश पेशेवर C ++ प्रोग्रामर इससे परिचित होंगे, लेकिन जैसा कि आप विभिन्न उद्योगों / परियोजनाओं में कहते हैं कि फोकस और अनुभव अलग-अलग हैं।
टोनी डेलारॉय

35

वास्तव में C ++ में कार्यों को स्थगित करने का एक तरीका है: किसी वस्तु के विध्वंसक का उपयोग करना।

यह मानते हुए कि आपके पास C ++ 11 तक पहुंच है:

class Defer {
public:
    Defer(std::function<void()> f): f_(std::move(f)) {}
    ~Defer() { if (f_) { f_(); } }

    void cancel() { f_ = std::function<void()>(); }

private:
    Defer(Defer const&) = delete;
    Defer& operator=(Defer const&) = delete;

    std::function<void()> f_;
}; // class Defer

और फिर उस उपयोगिता का उपयोग करते हुए:

int foo() {
    Defer const defer{&executeThisFunctionInAnyCase}; // or a lambda

    // ...

    if (!executeA()) { return 1; }

    // ...

    if (!executeB()) { return 2; }

    // ...

    if (!executeC()) { return 3; }

    // ...

    return 4;
} // foo

67
यह मेरे लिए सिर्फ पूर्ण आक्षेप है। मुझे वास्तव में समझ में नहीं आता कि इतने सारे C ++ प्रोग्रामर एक समस्या को हल करना क्यों पसंद करते हैं, जितनी संभव हो उतने भाषा सुविधाओं को टॉस करके, जब तक कि वह बिंदु जहां हर कोई भूल गया हो कि आप वास्तव में किस समस्या को हल कर रहे थे: उन्हें अब कोई परवाह नहीं है, क्योंकि वे हैं अब उन सभी विदेशी भाषा सुविधाओं का उपयोग करने में रुचि रखते हैं। और वहाँ से, आप अपने आप को मेटा कोड लिखने के दिनों और हफ्तों तक व्यस्त रख सकते हैं, फिर मेटा कोड को बनाए रख सकते हैं, फिर मेटा कोड को संभालने के लिए मेटा कोड लिखें।
लुंडिन

24
@ लुंडिन: ठीक है, मुझे समझ में नहीं आता है कि किसी को भंगुर कोड से कैसे संतुष्ट किया जा सकता है जो जल्द से जल्द टूट जाएगा / ब्रेक / रिटर्न पेश किया जाता है या एक अपवाद फेंक दिया जाता है। दूसरी ओर, यह समाधान भविष्य के रखरखाव के चेहरे में लचीला है, और केवल इस तथ्य पर निर्भर करता है कि विध्वंसक को अंजाम देने के दौरान निष्पादित किया जाता है जो सी ++ की सबसे महत्वपूर्ण विशेषता में से एक है। विदेशी के इस योग्यता को जबकि यह अंतर्निहित सिद्धांत है कि सभी मानक कंटेनरों को कम से कम कहने के लिए मनोरंजक है।
मैथ्यू एम।

7
@ लुंडिन: मैथ्यू के कोड का लाभ यह है कि एक अपवाद को फेंकने पर executeThisFunctionInAnyCase();भी निष्पादित होगा foo();। अपवाद-सुरक्षित कोड लिखते समय, इस तरह के सभी सफाई कार्यों को एक विध्वंसक में डालना अच्छा है।
ब्रायन

6
@Brian फिर किसी भी अपवाद को अंदर न फेंके foo()। और अगर करते भी हैं, तो पकड़ लेते हैं। समस्या सुलझ गयी। बग को ठीक करके उन्हें ठीक करें, काम के इर्द-गिर्द न लिखकर।
लुंडिन

18
@ लुंडिन: Deferवर्ग कोड का एक पुन: प्रयोज्य छोटा टुकड़ा है जो आपको अपवाद सुरक्षित तरीके से किसी भी अंत-ब्लॉक की सफाई करने देता है। इसे आमतौर पर स्कोप गार्ड के रूप में जाना जाता है । हाँ, स्कोप गार्ड के किसी भी उपयोग को अन्य अधिक मैनुअल तरीकों से व्यक्त किया जा सकता है, जैसे किसी भी forलूप को ब्लॉक और whileलूप के रूप में व्यक्त किया जा सकता है , जिसे बदले में व्यक्त किया जा सकता है ifऔर gotoयदि आप चाहें तो विधानसभा भाषा में व्यक्त किया जा सकता है, या उन लोगों के लिए जो सच्चे स्वामी हैं, विशेष शॉर्ट ग्रन्ट्स और मंत्रों के तितली प्रभाव द्वारा निर्देशित कॉस्मिक किरणों के माध्यम से बिट्स को स्मृति में बदलकर। लेकिन, ऐसा क्यों।
चीयर्स एंड हीथ। - अल्फ

34

एक अच्छी तकनीक है जिसे रिटर्न स्टेटमेंट (इटजैक्स द्वारा निर्धारित विधि) के साथ एक अतिरिक्त आवरण फ़ंक्शन की आवश्यकता नहीं है। यह एक while(0)छद्म पाश का उपयोग करता है । यह while (0)सुनिश्चित करता है कि यह वास्तव में लूप नहीं है लेकिन केवल एक बार निष्पादित किया गया है। हालांकि, लूप सिंटैक्स ब्रेक स्टेटमेंट के उपयोग की अनुमति देता है।

void foo()
{
  // ...
  do {
      if (!executeStepA())
          break;
      if (!executeStepB())
          break;
      if (!executeStepC())
          break;
  }
  while (0);
  // ...
}

11
कई रिटर्न वाले फ़ंक्शन का उपयोग करना मेरे विचार में समान, लेकिन अधिक पठनीय है।
लुंडिन

4
हां, निश्चित रूप से यह अधिक पठनीय है ... हालाँकि एक दक्षता बिंदु से अतिरिक्त फ़ंक्शन कॉल (पैरामीटर पासिंग और रिटर्न) ओवरहेड का निर्माण {} के साथ किया जा सकता है जबकि (0) निर्माण।
देबासीस

5
आप अभी भी कार्य करने के लिए स्वतंत्र हैं inline। वैसे भी, यह जानने के लिए एक अच्छी तकनीक है, क्योंकि यह इस समस्या से अधिक में मदद करता है।
रुस्लान

2
@ लुंडिन आपको कोड स्थानीयता को ध्यान में रखना होगा, बहुत से स्थानों पर कोड को फैलाना भी इसके मुद्दे हैं।
एपीआई-जानवर

3
मेरे अनुभव में, यह एक बहुत ही असामान्य मुहावरा है। मुझे यह पता लगाने में थोड़ा समय लगेगा कि बिल्ली क्या चल रही थी, और जब मैं कोड की समीक्षा कर रहा हूं तो यह एक बुरा संकेत है। यह देखते हुए कि यह अन्य, अधिक सामान्य और इसलिए अधिक पठनीय दृष्टिकोणों पर कोई लाभ नहीं है, मैं इस पर हस्ताक्षर नहीं कर सकता।
कोड़ी ग्रे

19

आप यह भी कर सकते हैं:

bool isOk = true;
std::vector<bool (*)(void)> funcs; //vector of function ptr

funcs.push_back(&executeStepA);
funcs.push_back(&executeStepB);
funcs.push_back(&executeStepC);
//...

//this will stop at the first false return
for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it) 
    isOk = (*it)();
if (isOk)
 //doSomeStuff
executeThisFunctionInAnyCase();

इस तरह आपके पास एक न्यूनतम रैखिक विकास आकार है, प्रति पंक्ति +1 लाइन, और यह आसानी से मुख्य है।


संपादित करें : (साभार @Unda) एक बड़ा प्रशंसक नहीं है क्योंकि आप दृश्यता को कम करते हैं IMO:

bool isOk = true;
auto funcs { //using c++11 initializer_list
    &executeStepA,
    &executeStepB,
    &executeStepC
};

for (auto it = funcs.begin(); it != funcs.end() && isOk; ++it) 
    isOk = (*it)();
if (isOk)
 //doSomeStuff
executeThisFunctionInAnyCase();

1
यह पुश_बैक () के अंदर होने वाले फंक्शनल फंक्शन कॉल्स के बारे में था, लेकिन आपने इसे वैसे भी तय कर लिया :)
क्वेंटिन

4
मुझे यह जानकर अच्छा लगेगा कि यह एक गिरावट क्यों है। निष्पादित चरणों को मानते हुए, वास्तव में सममित हैं, जैसा कि वे प्रतीत होते हैं, फिर यह साफ और बनाए रखने योग्य है।
क्लिक करें

1
हालांकि यह यकीनन क्लीनर लग सकता है। लोगों के लिए समझना कठिन हो सकता है, और संकलक के लिए समझना निश्चित रूप से कठिन है!
रॉय टी।

1
डेटा के रूप में अपने कार्यों को अधिक व्यवहार करना अक्सर एक अच्छा विचार होता है - एक बार जब आप इसे कर लेते हैं तो आप बाद के रिफ्लेक्टरिंग को भी नोटिस करेंगे। इससे भी बेहतर यह है कि आप जिस प्रत्येक टुकड़े को संभाल रहे हैं वह एक वस्तु संदर्भ है, न कि केवल एक फ़ंक्शन संदर्भ - यह आपको अपने कोड को लाइन में सुधार करने के लिए और भी अधिक क्षमता देगा।
बिल के

3
एक तुच्छ मामले के लिए थोड़ा अधिक इंजीनियर, लेकिन इस तकनीक में निश्चित रूप से एक अच्छी सुविधा है, जो दूसरों के लिए नहीं है: आप निष्पादन के क्रम को बदल सकते हैं और [क्रम] रनटाइम पर कॉल किए जाने वाले कार्य, और यह अच्छा है :)
Xocoatz

18

क्या यह काम करेगा? मुझे लगता है कि यह आपके कोड के बराबर है।

bool condition = true; // using only one boolean variable
if (condition) condition = executeStepA();
if (condition) condition = executeStepB();
if (condition) condition = executeStepC();
...
executeThisFunctionInAnyCase();

3
मैं आमतौर पर okइसी तरह के चर का उपयोग करते समय चर को बुलाता हूं ।
मैके

1
मुझे यह जानने में बहुत दिलचस्पी होगी कि चढ़ाव क्यों हैं। यहाँ क्या गलत हो रहा है?
एबीसीप्लस

1
साइक्लोमैटिक जटिलता के लिए शॉर्ट सर्किट दृष्टिकोण के साथ उर उत्तर की तुलना करें।
अल्फा गोकू

14

वांछित कोड मानकर मैं वर्तमान में इसे देख रहा हूं:

bool conditionA = executeStepA();
if (conditionA){
    bool conditionB = executeStepB();
    if (conditionB){
        bool conditionC = executeStepC();
        if (conditionC){
            ...
        }
    }
}    
executeThisFunctionInAnyCase();

मैं कहूंगा कि सही दृष्टिकोण, इसमें पढ़ना सबसे आसान है और इसे बनाए रखना सबसे आसान है, इसमें इंडेंटेशन के कम स्तर होंगे, जो (वर्तमान में) प्रश्न का घोषित उद्देश्य है।

// Pre-declare the variables for the conditions
bool conditionA = false;
bool conditionB = false;
bool conditionC = false;

// Execute each step only if the pre-conditions are met
conditionA = executeStepA();
if (conditionA)
    conditionB = executeStepB();
if (conditionB)
    conditionC = executeStepC();
if (conditionC) {
    ...
}

// Unconditionally execute the 'cleanup' part.
executeThisFunctionInAnyCase();

यह एस, अपवादों, डमी छोरों, या अन्य कठिन निर्माणों के लिए किसी भी आवश्यकता से बचा जाता है और बस हाथ में सरल नौकरी के साथ मिलता है।gotowhile


छोरों का उपयोग करते समय, इसका उपयोग आमतौर पर स्वीकार्य होता है returnऔर breakअतिरिक्त "ध्वज" चर पेश करने की आवश्यकता के बिना लूप से बाहर निकलने के लिए। इस मामले में, एक गोटो का उपयोग करना समान रूप से सहज होगा - याद रखें कि आप अतिरिक्त परिवर्तनशील परिवर्तनीयता के लिए अतिरिक्त गोटो जटिलता का व्यापार कर रहे हैं।
हुगोमग

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

बहुत उपयोगी दृष्टिकोण, esp। स्वयं के द्वारा उपयोग के लिए newbie, यह बिना किसी कमियां के एक क्लीनर समाधान प्रदान करता है। मैं ध्यान देता हूं कि stepsयह एक ही हस्ताक्षर या यहां तक ​​कि ब्लॉक के बजाय फ़ंक्शन होने पर निर्भर नहीं करता है । मैं इसे पहले पास रिफ्लेक्टर के रूप में इस्तेमाल किया जा सकता है, यहां तक ​​कि जहां एक अधिक परिष्कृत दृष्टिकोण मान्य है।
कीथ

12

क्या बयान को तोड़कर किसी तरह इस्तेमाल किया जा सकता है?

शायद सबसे अच्छा समाधान नहीं है लेकिन आप अपने बयानों को एक do .. while (0)लूप में रख सकते हैं और breakइसके बजाय बयानों का उपयोग कर सकते हैं return


2
यह मैं नहीं था जिसने इसे कम कर दिया, लेकिन यह एक ऐसी चीज़ के लिए निर्माण का दुरुपयोग होगा जहां प्रभाव वर्तमान में चाहता है लेकिन निश्चित रूप से दर्द में परिणाम देगा। संभवतः अगले डेवलपर के लिए जिसे आपको किसी अन्य प्रोजेक्ट पर ले जाने के बाद 2 साल के समय में इसे बनाए रखना होगा।
क्लिकरिक

3
do .. while (0)मैक्रो परिभाषाओं के लिए @ClickRick का उपयोग लूप का दुरुपयोग भी कर रहा है लेकिन इसे ठीक माना जाता है।
ahउह

1
शायद, लेकिन इसे प्राप्त करने के लिए क्लीनर तरीके हैं।
क्लिकरिक

4
@ मेरे उत्तर का एकमात्र उद्देश्य है उत्तर देना कि किसी भी तरह से कथन का उपयोग किया जा सकता है और उत्तर हाँ है, मेरे उत्तर में पहला शब्द बताता है कि यह उपयोग करने का समाधान नहीं हो सकता है।
१ou

2
यह उत्तर सिर्फ एक टिप्पणी होना चाहिए
msmucker0527

12

आप सभी ifशर्तें रख सकते हैं, स्वरूपित करें जैसा कि आप अपने स्वयं के फ़ंक्शन में चाहते हैं, रिटर्न ऑन executeThisFunctionInAnyCase()फ़ंक्शन निष्पादित करते हैं।

ओपी में आधार उदाहरण से, हालत परीक्षण और निष्पादन को इस तरह से विभाजित किया जा सकता है;

void InitialSteps()
{
  bool conditionA = executeStepA();
  if (!conditionA)
    return;
  bool conditionB = executeStepB();
  if (!conditionB)
    return;
  bool conditionC = executeStepC();
  if (!conditionC)
    return;
}

और फिर इस तरह से बुलाया;

InitialSteps();
executeThisFunctionInAnyCase();

यदि C ++ 11 लैम्ब्डा उपलब्ध हैं (ओपी में कोई C ++ 11 टैग नहीं था, लेकिन वे अभी भी एक विकल्प हो सकते हैं), तो हम अलग समारोह को वापस कर सकते हैं और इसे लैम्ब्डा में लपेट सकते हैं।

// Capture by reference (variable access may be required)
auto initialSteps = [&]() {
  // any additional code
  bool conditionA = executeStepA();
  if (!conditionA)
    return;
  // any additional code
  bool conditionB = executeStepB();
  if (!conditionB)
    return;
  // any additional code
  bool conditionC = executeStepC();
  if (!conditionC)
    return;
};

initialSteps();
executeThisFunctionInAnyCase();

10

यदि आप लूप को नापसंद gotoऔर नापसंद करते हैं और do { } while (0);C ++ का उपयोग करना पसंद करते हैं तो आप एक ही प्रभाव के लिए एक अस्थायी लैंबडा का उपयोग कर सकते हैं।

[&]() { // create a capture all lambda
  if (!executeStepA()) { return; }
  if (!executeStepB()) { return; }
  if (!executeStepC()) { return; }
}(); // and immediately call it

executeThisFunctionInAnyCase();

1
if आप नापसंद && करते हैं, तो आप नापसंद करते हैं {} जबकि (0) && आप C ++ को पसंद करते हैं ... क्षमा करें, विरोध नहीं कर सकते, लेकिन अंतिम स्थिति विफल रहती है क्योंकि प्रश्न c और साथ ही c ++ पर क्लिक किया जाता है
ClickRick

@ क्लिक करें हर किसी को खुश करना हमेशा मुश्किल होता है। में मेरी राय C / C ++ आप आमतौर पर उन दोनों में से किसी एक और अन्य के उपयोग में कोड सिकोड़ी है के रूप में ऐसी कोई बात नहीं है।
एलेक्स

9

आपके कोड में IF / ELSE की श्रृंखला भाषा का मुद्दा नहीं है, बल्कि आपके प्रोग्राम का डिज़ाइन है। यदि आप अपने कार्यक्रम को पुनः-कारक या फिर से लिखने में सक्षम हैं, तो मैं सुझाव देना चाहता हूं कि आप बेहतर समाधान खोजने के लिए डिज़ाइन पैटर्न ( http://sourcemaking.com/design_patterns ) में देखें

आमतौर पर, जब आप IF के बहुत से और अपने कोड में देखते हैं, तो यह रणनीति डिजाइन पैटर्न ( http://sourcemaking.com/design_patterns/strategy/c-sharp-dot-net ) या शायद एक संयोजन को लागू करने का अवसर है अन्य पैटर्न के।

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


9

तुम बस यही करो ।।

coverConditions();
executeThisFunctionInAnyCase();

function coverConditions()
 {
 bool conditionA = executeStepA();
 if (!conditionA) return;
 bool conditionB = executeStepB();
 if (!conditionB) return;
 bool conditionC = executeStepC();
 if (!conditionC) return;
 }

100 में से 99 बार, यह ऐसा करने का एकमात्र तरीका है।

कभी, कभी, कभी कंप्यूटर कोड में "मुश्किल" कुछ करने की कोशिश करें।


वैसे, मुझे पूरा यकीन है कि निम्नलिखित वास्तविक समाधान आपके मन में था ...

जारी रखने के बयान एल्गोरिथम प्रोग्रामिंग में महत्वपूर्ण है। (जैसा कि, गोटो बयान एल्गोरिथम प्रोग्रामिंग में महत्वपूर्ण है।)

कई प्रोग्रामिंग भाषाओं में आप ऐसा कर सकते हैं:

-(void)_testKode
    {
    NSLog(@"code a");
    NSLog(@"code b");
    NSLog(@"code c\n");
    
    int x = 69;
    
    {
    
    if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
    
    if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
    
    if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
    
    }
    
    NSLog(@"code g");
    }

(सबसे पहले ध्यान दें: उस उदाहरण जैसे नग्न ब्लॉक सुंदर कोड लिखने का एक महत्वपूर्ण और महत्वपूर्ण हिस्सा हैं, खासकर यदि आप "एल्गोरिथम" प्रोग्रामिंग के साथ काम कर रहे हैं।)

फिर, यह वही है जो आपके सिर में था, है ना? और यह लिखने का सुंदर तरीका है, इसलिए आपके पास अच्छी प्रवृत्ति है।

हालाँकि, दुखद रूप से, में उद्देश्य-ग मौजूदा संस्करण में (असाइड - मैं स्विफ्ट के बारे में नहीं जानता, क्षमा करें) एक अदृश्य सुविधा है जहां यह जांचता है कि क्या संलग्नक ब्लॉक लूप है।

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

यहाँ है कि तुम कैसे चारों ओर हो ...

-(void)_testKode
    {
    NSLog(@"code a");
    NSLog(@"code b");
    NSLog(@"code c\n");
    
    int x = 69;
    
    do{
    
    if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
    
    if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
    
    if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
    
    }while(false);
    
    NSLog(@"code g");
    }

तो यह मत भूलना ..

do {} जबकि (झूठा);

बस इसका मतलब है "इस ब्लॉक को एक बार करें"।

यानी, लिखने do{}while(false);और बस लिखने के बीच कोई अंतर नहीं है{}

यह अब पूरी तरह से काम करता है जैसा आप चाहते थे ... यहाँ आउटपुट है ...

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

तो, यह संभव है कि आप अपने सिर में एल्गोरिदम कैसे देखें। आपको हमेशा यह लिखने की कोशिश करनी चाहिए कि आपके सिर में क्या है। (विशेष रूप से अगर आप शांत नहीं हैं, क्योंकि जब सुंदर बाहर आता है! :)

"एलगोरिदमिक" परियोजनाओं में, जहां यह बहुत कुछ होता है, उद्देश्य-सी में, हमारे पास हमेशा एक मैक्रो जैसा होता है ...

#define RUNONCE while(false)

... तो आप ऐसा कर सकते हैं ...

-(void)_testKode
    {
    NSLog(@"code a");
    int x = 69;
    
    do{
    if ( x == 13 )
        {
        NSLog(@"code d---\n");
        continue;
        }
    if ( x == 69 )
        {
        NSLog(@"code e---\n");
        continue;
        }
    if ( x == 13 )
        {
        NSLog(@"code f---\n");
        continue;
        }
    }RUNONCE
    
    NSLog(@"code g");
    }

दो बिंदु हैं:

हालांकि, यह मूर्खतापूर्ण है कि उद्देश्य-सी एक जारी बयान में ब्लॉक के प्रकार की जांच करता है, यह "उस लड़ाई" के लिए परेशान है। इसलिए यह एक कठिन निर्णय है।

बी, सवाल यह है कि आपको इंडेंट करना चाहिए, उदाहरण में, वह ब्लॉक? मैं इस तरह के सवालों पर नींद खो देता हूं, इसलिए मैं सलाह नहीं दे सकता।

आशा करता हूँ की ये काम करेगा।



क्या आपने अधिक प्रतिनिधि पाने के लिए इनाम रखा? :)
टीएमएस

उन सभी टिप्पणियों को डालने के बजाय if, आप अधिक वर्णनात्मक फ़ंक्शन नामों का उपयोग कर सकते हैं और टिप्पणियों को फ़ंक्शन में डाल सकते हैं।
थॉमस अहले

छी। मैं 1 लाइन समाधान ले जाऊंगा, जो शॉर्ट सर्किट मूल्यांकन (जो 20 साल से अधिक समय से भाषाओं में है और अच्छी तरह से ज्ञात है) के साथ किसी भी दिन इस गड़बड़ी के बारे में संक्षिप्त रूप से पढ़ता है। मुझे लगता है कि हम दोनों सहमत हैं कि हम एक-दूसरे के साथ काम न करके खुश हैं।
M2tM

8

यदि आपके निष्पादन कार्य एक अपवाद को फेंक देते हैं यदि वे झूठे लौटने के बजाय विफल हो जाते हैं। तब आपका कॉलिंग कोड इस तरह दिख सकता है:

try {
    executeStepA();
    executeStepB();
    executeStepC();
}
catch (...)

निश्चित रूप से मैं यह मान रहा हूं कि आपके मूल उदाहरण में निष्पादन कदम केवल कदम के अंदर हुई त्रुटि के मामले में झूठा वापस आएगा?


3
नियंत्रण प्रवाह के अपवाद का उपयोग करना अक्सर बुरा व्यवहार और बदबूदार कोड माना जाता है
user902383

8

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

bool ok = true;
bool conditionA = executeStepA();
// ... possibly edit conditionA, or just ok &= executeStepA();
ok &= conditionA;

if (ok) {
    bool conditionB = executeStepB();
    // ... possibly do more stuff
    ok &= conditionB;
}
if (ok) {
    bool conditionC = executeStepC();
    ok &= conditionC;
}
if (ok && additionalCondition) {
    // ...
}

executeThisFunctionInAnyCase();
// can now also:
return ok;

ok &= conditionX;बस और क्यों नहीं ok = conditionX;?
एबीसीप्लस

@ user3253359 कई मामलों में, हां, आप ऐसा कर सकते हैं। यह एक वैचारिक डेमो है; वर्किंग कोड में हम इसे जितना संभव हो उतना सरल बनाने की कोशिश करेंगे
blgt

+1 कुछ साफ और रख-रखाव वाले जवाबों में से एक, जो सी में काम करता है , जैसा कि प्रश्न में निर्धारित है।
क्लिकरिक

6

C ++ में (प्रश्न C और C ++ दोनों को टैग किया गया है), यदि आप अपवादों का उपयोग करने के लिए फ़ंक्शन नहीं बदल सकते हैं, तो आप अभी भी अपवाद तंत्र का उपयोग कर सकते हैं यदि आप थोड़ा सहायक फ़ंक्शन लिखते हैं जैसे

struct function_failed {};
void attempt(bool retval)
{
  if (!retval)
    throw function_failed(); // or a more specific exception class
}

तब आपका कोड निम्नानुसार पढ़ सकता था:

try
{
  attempt(executeStepA());
  attempt(executeStepB());
  attempt(executeStepC());
}
catch (function_failed)
{
  // -- this block intentionally left empty --
}

executeThisFunctionInAnyCase();

यदि आप फैंसी सिंटैक्स में हैं, तो आप इसके बजाय स्पष्ट कलाकारों के माध्यम से काम कर सकते हैं:

struct function_failed {};
struct attempt
{
  attempt(bool retval)
  {
    if (!retval)
      throw function_failed();
  }
};

फिर आप अपना कोड इस प्रकार लिख सकते हैं

try
{
  (attempt) executeStepA();
  (attempt) executeStepB();
  (attempt) executeStepC();
}
catch (function_failed)
{
  // -- this block intentionally left empty --
}

executeThisFunctionInAnyCase();

अपवादों में रिफ्लेक्टरिंग वैल्यू चेक करना जरूरी नहीं है कि जाने के लिए एक अच्छा तरीका है, अपवादों को कम करने के लिए पर्याप्त ओवरहेड है।
जॉन वू

4
-1 C ++ में सामान्य प्रवाह के लिए अपवादों का उपयोग करना इस तरह की खराब प्रोग्रामिंग प्रथा है। C ++ में अपवाद असाधारण परिस्थितियों के लिए आरक्षित होना चाहिए।
जैक एडले

1
प्रश्न पाठ से (मेरे द्वारा जोर देने पर): "कार्य निष्पादन निष्पादित किया जाना चाहिए और यदि केवल पिछले सफल हुआ है तो निष्पादित किया जाना चाहिए " दूसरे शब्दों में, विफलता को इंगित करने के लिए रिटर्न वैल्यू का उपयोग किया जाता है। है यही कारण है, इस त्रुटि से निपटने है (और एक आशा करता हूं कि विफलताओं हैं असाधारण)। त्रुटि निवारण है वास्तव में क्या अपवाद के लिए आविष्कार किया गया।
celtschk

1
नहीं। सबसे पहले, त्रुटि प्रसार की अनुमति देने के लिए अपवाद बनाए गए थे , न कि त्रुटि से निपटने ; दूसरी बात, "फंक्शंस एक्ज़ीक्यूटस्टेपएक्स को निष्पादित किया जाना चाहिए, यदि और केवल पिछले सफल होने पर।" इसका मतलब यह नहीं है कि पिछले फ़ंक्शन द्वारा वापस की गई बूलियन झूठी स्पष्ट रूप से असाधारण / गलत मामले को इंगित करती है। इस प्रकार आपका कथन गैर अनुक्रमिक है । त्रुटि से निपटने और प्रवाह स्वच्छता को कई अलग-अलग तरीकों से लागू किया जा सकता है, अपवाद एक उपकरण है जो त्रुटि प्रसार और आउट-ऑफ-प्लेस त्रुटि हैंडलिंग और उस पर एक्सेल करने की अनुमति देता है।

6

यदि आपका कोड आपके उदाहरण के समान सरल है और आपकी भाषा शॉर्ट-सर्किट मूल्यांकन का समर्थन करती है, तो आप यह कोशिश कर सकते हैं:

StepA() && StepB() && StepC() && StepD();
DoAlways();

यदि आप अपने कार्यों के लिए तर्क दे रहे हैं और अन्य परिणाम प्राप्त कर रहे हैं ताकि आपका कोड पिछले फैशन में नहीं लिखा जा सके, तो अन्य उत्तरों में से कई बेहतर समस्या के अनुकूल होंगे।


वास्तव में मैंने इस विषय को बेहतर ढंग से समझाने के लिए अपने प्रश्न को संपादित किया, लेकिन अधिकांश उत्तरों को अमान्य नहीं करने के लिए इसे अस्वीकार कर दिया गया। : \
ABCplus

मैं SO में एक नया उपयोगकर्ता हूँ, और एक नौसिखिया प्रोग्रामर। 2 प्रश्न तब: क्या यह जोखिम है कि आपके द्वारा कहा गया एक अन्य प्रश्न इस प्रश्न के कारण डुप्लिकेट के रूप में चिह्नित किया जाएगा? एक और बिंदु है: एक नौसिखिया SO उपयोगकर्ता / प्रोग्रामर कैसे वहाँ सभी के बीच सबसे अच्छा जवाब चुन सकता है (लगभग अच्छा मैं मान सकता हूँ ..)?
ABCplus

6

C ++ 11 और उससे आगे के लिए, एक अच्छा दृष्टिकोण डी के दायरे (निकास) तंत्र के समान एक गुंजाइश निकास प्रणाली को लागू करने के लिए हो सकता है ।

इसे लागू करने का एक संभावित तरीका C ++ 11 लैम्ब्डा और कुछ हेल्पर मैक्रोज़ का उपयोग करना है:

template<typename F> struct ScopeExit 
{
    ScopeExit(F f) : fn(f) { }
    ~ScopeExit() 
    { 
         fn();
    }

    F fn;
};

template<typename F> ScopeExit<F> MakeScopeExit(F f) { return ScopeExit<F>(f); };

#define STR_APPEND2_HELPER(x, y) x##y
#define STR_APPEND2(x, y) STR_APPEND2_HELPER(x, y)

#define SCOPE_EXIT(code)\
    auto STR_APPEND2(scope_exit_, __LINE__) = MakeScopeExit([&](){ code })

यह आपको फ़ंक्शन से जल्दी लौटने और यह सुनिश्चित करने की अनुमति देगा कि जो भी सफाई कोड आप परिभाषित करते हैं, वह हमेशा गुंजाइश से बाहर निकलने पर निष्पादित होता है:

SCOPE_EXIT(
    delete pointerA;
    delete pointerB;
    close(fileC); );

if (!executeStepA())
    return;

if (!executeStepB())
    return;

if (!executeStepC())
    return;

मैक्रोज़ वास्तव में सिर्फ सजावट हैं। MakeScopeExit()सीधे इस्तेमाल किया जा सकता है।


इस काम को करने के लिए मैक्रोज़ की ज़रूरत नहीं है। और [=]आमतौर पर एक स्कोप्ड लैम्ब्डा के लिए गलत है।
यक्क - एडम नेवरामोंट

हां, मैक्रोज़ सिर्फ सजावट के लिए हैं और उन्हें फेंक दिया जा सकता है। लेकिन क्या आप यह नहीं कहेंगे कि मूल्य पर कब्जा सबसे सुरक्षित "सामान्य" दृष्टिकोण है?
19:00 पर ग्लैमर्ट

1
नहीं: यदि आपका लैम्ब्डा मौजूदा स्कोप से आगे नहीं रहेगा, जहां लैम्ब्डा बनाया गया है, उपयोग करें [&]: यह सुरक्षित है, और कम से कम आश्चर्यजनक है। मूल्य द्वारा कैप्चर केवल जब लंबोदर (या प्रतियां) घोषणा के बिंदु पर दायरे से अधिक समय तक जीवित रह सकता है ...
यक - एडम नेवरामॉन्ट

हाँ, यह समझ में आता है। मैं इसे बदल दूंगा। धन्यवाद!
ग्लैम्पर्ट

6

कोई भी सबसे सरल समाधान क्यों नहीं दे रहा है? : डी

यदि आपके सभी कार्यों में एक ही हस्ताक्षर है तो आप इसे इस तरह से कर सकते हैं (C भाषा के लिए):

bool (*step[])() = {
    &executeStepA,
    &executeStepB,
    &executeStepC,
    ... 
};

for (int i = 0; i < numberOfSteps; i++) {
    bool condition = step[i]();

    if (!condition) {
        break;
    }
}

executeThisFunctionInAnyCase();

एक साफ सी ++ समाधान के लिए, आपको एक इंटरफ़ेस वर्ग बनाना चाहिए जिसमें एक निष्पादन विधि होती है और वस्तुओं में आपके चरणों को लपेटता है।
फिर, उपरोक्त समाधान इस तरह दिखेगा:

Step *steps[] = {
    stepA,
    stepB,
    stepC,
    ... 
};

for (int i = 0; i < numberOfSteps; i++) {
    Step *step = steps[i];

    if (!step->execute()) {
        break;
    }
}

executeThisFunctionInAnyCase();

5

यह मानते हुए कि आपको व्यक्तिगत स्थिति चर की आवश्यकता नहीं है, परीक्षणों को सम्मिलित करते हुए और "ओके" पथ के रूप में फ़ालतू का उपयोग करके, यदि आप / और कथनों का अधिक ऊर्ध्वाधर सेट प्राप्त करने की अनुमति देंगे:

bool failed = false;

// keep going if we don't fail
if (failed = !executeStepA())      {}
else if (failed = !executeStepB()) {}
else if (failed = !executeStepC()) {}
else if (failed = !executeStepD()) {}

runThisFunctionInAnyCase();

असफल चर को स्वीकार करने से कोड थोड़ा बहुत IMO अस्पष्ट हो जाता है।

अंदर के चर की घोषणा करना ठीक है, = बनाम == के बारे में कोई चिंता नहीं है।

// keep going if we don't fail
if (bool failA = !executeStepA())      {}
else if (bool failB = !executeStepB()) {}
else if (bool failC = !executeStepC()) {}
else if (bool failD = !executeStepD()) {}
else {
     // success !
}

runThisFunctionInAnyCase();

यह अस्पष्ट है, लेकिन कॉम्पैक्ट है:

// keep going if we don't fail
if (!executeStepA())      {}
else if (!executeStepB()) {}
else if (!executeStepC()) {}
else if (!executeStepD()) {}
else { /* success */ }

runThisFunctionInAnyCase();

5

यह एक राज्य मशीन की तरह दिखता है, जो आसान है क्योंकि आप इसे राज्य-पैटर्न के साथ आसानी से लागू कर सकते हैं ।

जावा में यह कुछ इस तरह दिखेगा:

interface StepState{
public StepState performStep();
}

एक कार्यान्वयन निम्नानुसार काम करेगा:

class StepA implements StepState{ 
    public StepState performStep()
     {
         performAction();
         if(condition) return new StepB()
         else return null;
     }
}

और इसी तरह। तो आप के साथ बड़ा विकल्प अगर हालत के साथ कर सकते हैं:

Step toDo = new StepA();
while(toDo != null)
      toDo = toDo.performStep();
executeThisFunctionInAnyCase();

5

जैसा कि रोमिक ने उल्लेख किया है, आप इसके लिए एक डिज़ाइन पैटर्न लागू कर सकते हैं, लेकिन मैं रणनीति के बजाय डेकोरेटर पैटर्न का उपयोग करूँगा क्योंकि आप चेन कॉल करना चाहते हैं। यदि कोड सरल है, तो मैं घोंसले को रोकने के लिए अच्छी तरह से संरचित उत्तरों में से एक के साथ जाऊंगा। हालांकि, अगर यह जटिल है या गतिशील चेनिंग की आवश्यकता है, तो डेकोरेटर पैटर्न एक अच्छा विकल्प है। यहाँ एक yUML वर्ग आरेख है :

YUML वर्ग आरेख

यहाँ एक नमूना LinqPad C # कार्यक्रम है:

void Main()
{
    IOperation step = new StepC();
    step = new StepB(step);
    step = new StepA(step);
    step.Next();
}

public interface IOperation 
{
    bool Next();
}

public class StepA : IOperation
{
    private IOperation _chain;
    public StepA(IOperation chain=null)
    {
        _chain = chain;
    }

    public bool Next() 
    {
        bool localResult = false;
        //do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to true
        localResult = true;
        Console.WriteLine("Step A success={0}", localResult);

        //then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}

public class StepB : IOperation
{
    private IOperation _chain;
    public StepB(IOperation chain=null)
    {
        _chain = chain;
    }

    public bool Next() 
    {   
        bool localResult = false;

        //do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to false, 
            // to show breaking out of the chain
        localResult = false;
        Console.WriteLine("Step B success={0}", localResult);

        //then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}

public class StepC : IOperation
{
    private IOperation _chain;
    public StepC(IOperation chain=null)
    {
        _chain = chain;
    }

    public bool Next() 
    {
        bool localResult = false;
        //do work
        //...
        // set localResult to success of this work
        // just for this example, hard coding to true
        localResult = true;
        Console.WriteLine("Step C success={0}", localResult);
        //then call next in chain and return
        return (localResult && _chain != null) 
            ? _chain.Next() 
            : true;
    }
}

डिजाइन पैटर्न पर पढ़ने के लिए सबसे अच्छी किताब, IMHO, हेड फर्स्ट डिज़ाइन पैटर्न है


जेफरी के जवाब की तरह इस पर क्या फायदा है?
दासन

बहुत अधिक परिवर्तन लचीला, जब आवश्यकताओं में परिवर्तन होता है तो यह बहुत सारे डोमेन ज्ञान के बिना प्रबंधित करने के लिए सरल होता है। खासकर जब आप विचार करते हैं कि नेस्टेड इफ्स के कुछ खंड कितने गहरे और लंबे हो सकते हैं। यह सभी बहुत नाजुक हो सकता है और इस प्रकार इसके साथ काम करने के लिए उच्च जोखिम है। मुझे गलत मत समझिए कुछ अनुकूलन परिदृश्यों के परिणामस्वरूप आपको यह छूट देनी पड़ सकती है और इफ़्स पर वापस जा सकते हैं लेकिन 99% यह ठीक है। लेकिन बात यह है कि जब आप उस स्तर पर पहुंच जाते हैं तो आपको उस स्थिरता की परवाह नहीं होती है जिसके लिए आपको प्रदर्शन की आवश्यकता होती है।
जॉन निकोलस

4

कई उत्तरों ने एक पैटर्न पर संकेत दिया जो मैंने देखा और कई बार उपयोग किया, विशेष रूप से नेटवर्क प्रोग्रामिंग में। नेटवर्क स्टैक में अक्सर अनुरोधों का एक लंबा क्रम होता है, जिनमें से कोई भी विफल हो सकता है और प्रक्रिया को रोक देगा।

सामान्य पैटर्न का उपयोग करना था do { } while (false);

मैंने while(false)इसे बनाने के लिए एक मैक्रो का उपयोग किया do { } once;। यह सामान्य पैटर्न था:

do
{
    bool conditionA = executeStepA();
    if (! conditionA) break;
    bool conditionB = executeStepB();
    if (! conditionB) break;
    // etc.
} while (false);

यह पैटर्न पढ़ना अपेक्षाकृत आसान था, और उन वस्तुओं का उपयोग करने की अनुमति देता था जो ठीक से नष्ट हो जाती थीं और कई रिटर्न देने से बचती थीं और कदम बढ़ाना और थोड़ा आसान हो जाता था।


4

मैथ्यू के C ++ 11 उत्तर पर सुधार करने और इसके उपयोग के माध्यम से होने वाली रनटाइम लागत से बचने के लिए std::function, मैं निम्नलिखित का उपयोग करने का सुझाव दूंगा

template<typename functor>
class deferred final
{
public:
    template<typename functor2>
    explicit deferred(functor2&& f) : f(std::forward<functor2>(f)) {}
    ~deferred() { this->f(); }

private:
    functor f;
};

template<typename functor>
auto defer(functor&& f) -> deferred<typename std::decay<functor>::type>
{
    return deferred<typename std::decay<functor>::type>(std::forward<functor>(f));
}

यह सरल टेम्प्लेट क्लास किसी भी फ़ंक्टर को स्वीकार करेगी जिसे बिना किसी पैरामीटर के कहा जा सकता है, और ऐसा किसी भी गतिशील मेमोरी आवंटन के बिना होता है और इसलिए अनावश्यक ओवरहेड के बिना सी ++ के अमूर्त लक्ष्य के लिए बेहतर अनुरूप होता है। अतिरिक्त फ़ंक्शन टेम्पलेट टेम्पलेट पैरामीटर कटौती द्वारा उपयोग को आसान बनाने के लिए है (जो वर्ग टेम्पलेट मापदंडों के लिए उपलब्ध नहीं है)

उपयोग उदाहरण:

auto guard = defer(executeThisFunctionInAnyCase);
bool conditionA = executeStepA();
if (!conditionA) return;
bool conditionB = executeStepB();
if (!conditionB) return;
bool conditionC = executeStepC();
if (!conditionC) return;

जिस तरह मैथ्यू का जवाब है कि यह समाधान पूरी तरह से सुरक्षित है, और executeThisFunctionInAnyCaseसभी मामलों में बुलाया जाएगा। executeThisFunctionInAnyCaseखुद को फेंक देना चाहिए , विनाशकों को स्पष्ट रूप से चिह्नित किया जाता है noexceptऔर इसलिए std::terminateस्टैक बाइंडिंग के दौरान एक अपवाद को फेंकने के बजाय कॉल जारी किया जाएगा।


+1 मैं इस उत्तर की तलाश में था इसलिए मुझे इसे पोस्ट नहीं करना पड़ेगा। आपको 'डी कंस्ट्रक्टर' functorमें परफेक्ट-फॉरवर्ड करना चाहिए deferred, कोई जबरदस्ती करने की जरूरत नहीं है move
यक्क - एडम नेवरामोंट

@Yakk ने कंस्ट्रक्टर को फॉरवर्डिंग कंस्ट्रक्टर में बदल दिया
जो

3

ऐसा लगता है जैसे आप अपनी सारी कॉल एक ही ब्लॉक से करना चाहते हैं। जैसा कि अन्य ने इसे प्रस्तावित किया है, आपको या तो एक whileलूप का उपयोग करना चाहिए और उपयोग करना छोड़ना चाहिए breakया एक नया फ़ंक्शन जिसे आप छोड़ सकते हैं return(क्लीनर हो सकता है)।

मैं व्यक्तिगत रूप से निर्वासित हूं goto, यहां तक ​​कि फ़ंक्शन निकास के लिए भी। डिबगिंग करते समय वे कठिन हैं।

एक सुरुचिपूर्ण विकल्प जो आपके वर्कफ़्लो के लिए काम करना चाहिए, इस पर एक फ़ंक्शन सरणी और पुनरावृति बनाना है।

const int STEP_ARRAY_COUNT = 3;
bool (*stepsArray[])() = {
   executeStepA, executeStepB, executeStepC
};

for (int i=0; i<STEP_ARRAY_COUNT; ++i) {
    if (!stepsArray[i]()) {
        break;
    }
}

executeThisFunctionInAnyCase();

सौभाग्य से, डिबगर उन्हें आपके लिए स्पॉट करता है। यदि आप कोड के माध्यम से डिबगिंग और सिंगल-स्टेपिंग नहीं कर रहे हैं, तो आप इसे गलत कर रहे हैं।
कोड़ी ग्रे

मुझे समझ में नहीं आ रहा है कि आपका क्या मतलब है, न ही मैं सिंगल-स्टेपिंग का उपयोग क्यों नहीं कर सका?
AxFab

3

क्योंकि आपके पास निष्पादन के बीच [... कोड ऑफ ब्लॉक ...] है , मुझे लगता है कि आपके पास मेमोरी आवंटन या ऑब्जेक्ट इनिशियलाइज़ेशन है। इस तरह से आपको सभी सफाई के बारे में ध्यान रखना होगा जो आपने पहले से ही बाहर निकलने पर शुरू किया था, और इसे साफ भी करें यदि आप समस्या को पूरा करेंगे और कोई भी कार्य गलत होगा।

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

class CondA
{
public:
    CondA() { 
        if (!executeStepA()) 
            throw int(1);
        [Initialize data]
    }
    ~CondA() {        
        [Clean data]
    }
    A* _a;
};

class CondB : public CondA
{
public:
    CondB() { 
        if (!executeStepB()) 
            throw int(2);
        [Initialize data]
    }
    ~CondB() {        
        [Clean data]
    }
    B* _b;
};

class CondC : public CondB
{
public:
    CondC() { 
        if (!executeStepC()) 
            throw int(3);
        [Initialize data]
    }
    ~CondC() {        
        [Clean data]
    }
    C* _c;
};

और फिर आपके कोड में आपको कॉल करने की आवश्यकता है:

shared_ptr<CondC> C(nullptr);
try{
    C = make_shared<CondC>();
}
catch(int& e)
{
    //do something
}
if (C != nullptr)
{
   C->a;//work with
   C->b;//work with
   C->c;//work with
}
executeThisFunctionInAnyCase();

मुझे लगता है कि यह सबसे अच्छा समाधान है अगर सशर्त की हर कॉल किसी चीज को शुरू करती है, स्मृति और आदि को सबसे अच्छा यकीन है कि सब कुछ साफ हो जाएगा।


3

एक दिलचस्प तरीका अपवादों के साथ काम करना है।

try
{
    executeStepA();//function throws an exception on error
    ......
}
catch(...)
{
    //some error handling
}
finally
{
    executeThisFunctionInAnyCase();
}

यदि आप ऐसा कोड लिखते हैं तो आप किसी तरह गलत दिशा में जा रहे हैं। मैं इस तरह के कोड के लिए "समस्या" के रूप में नहीं देखूंगा, लेकिन इस तरह के एक गड़बड़ "वास्तुकला" के लिए।

युक्ति: एक अनुभवी डेवलपर के साथ उन मामलों पर चर्चा करें जिन पर आप भरोसा करते हैं ;-)


मुझे लगता है कि यह विचार हर इफ-चेन को प्रतिस्थापित नहीं कर सकता है। वैसे भी, कई मामलों में, यह एक बहुत अच्छा तरीका है!
WoIIe

3

एक अन्य दृष्टिकोण - do - whileलूप, भले ही इसका उल्लेख पहले किया गया था लेकिन इसका कोई उदाहरण नहीं था जो यह दिखाता है कि यह कैसा दिखता है:

do
{
    if (!executeStepA()) break;
    if (!executeStepB()) break;
    if (!executeStepC()) break;
    ...

    break; // skip the do-while condition :)
}
while (0);

executeThisFunctionInAnyCase();

(वैसे whileलूप के साथ पहले से ही एक उत्तर है, लेकिन do - whileलूप वास्तव में सही (शुरुआत में) की जांच नहीं करता है, बल्कि अंत में एक्सडी (यह छोड़ दिया जा सकता है, हालांकि)।


हे ज़फ्फी - इस उत्तर में ({) झूठे दृष्टिकोण के साथ {} के बड़े पैमाने पर स्पष्टीकरण हैं। stackoverflow.com/a/24588605/294884 दो अन्य उत्तरों ने भी इसका उल्लेख किया।
फेटी

यह एक आकर्षक सवाल है कि क्या इस स्थिति में CONTINUE या BREAK का उपयोग करना अधिक सुरुचिपूर्ण है!
फेटी

अरे @JoeBlow मैंने सभी उत्तर देखे ... सिर्फ इसके बारे में बात करने के बजाय दिखाना चाहता था :)
ज़फ्फी

यहाँ मेरा पहला जवाब मैंने कहा "किसी ने इस का उल्लेख नहीं किया है ..." और तुरंत किसी ने कहा कि यह दूसरा शीर्ष उत्तर था :)
फेटी

@JoeBlow एह, तुम सही हो। मैं ठीक करने की कोशिश करूँगा। मुझे ऐसा लग रहा है ... xD वैसे भी धन्यवाद, अगली बार मैं ध्यान थोड़ा और अधिक
दूंगा
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.