क्या किसी फंक्शन में केवल एक रिटर्न स्टेटमेंट होना चाहिए?


780

क्या ऐसे अच्छे कारण हैं कि किसी फ़ंक्शन में केवल एक रिटर्न स्टेटमेंट रखना बेहतर अभ्यास है?

या यह एक समारोह से लौटने के लिए ठीक है जैसे ही यह तार्किक रूप से ऐसा करने के लिए सही है, जिसका अर्थ है कि फ़ंक्शन में कई रिटर्न स्टेटमेंट हो सकते हैं?


25
मैं इस बात से सहमत नहीं हूं कि प्रश्न भाषा अज्ञेय है। कुछ भाषाओं के साथ, कई रिटर्न अधिक प्राकृतिक और दूसरों की तुलना में सुविधाजनक हैं। मैं एक C फ़ंक्शन की तुलना में C ++ में प्रारंभिक रिटर्न के बारे में शिकायत करने की अधिक संभावना रखता हूं जो RAII का उपयोग करता है।
एड्रियन मैकार्थी

3
यह बारीकी से संबंधित है और उत्कृष्ट उत्तर है: programmers.stackexchange.com/questions/118703/…
टिम श्मेल्टर

भाषा-नास्तिक? कार्यात्मक भाषा का उपयोग करने वाले किसी व्यक्ति को समझाएं कि उसे प्रति फ़ंक्शन एक रिटर्न का उपयोग करना चाहिए: पी
बोइथियोस

जवाबों:


741

मेरे पास अक्सर "आसान" स्थितियों के लिए लौटने के लिए एक विधि की शुरुआत में कई कथन हैं। उदाहरण के लिए, यह:

public void DoStuff(Foo foo)
{
    if (foo != null)
    {
        ...
    }
}

... इस तरह अधिक पठनीय (IMHO) बनाया जा सकता है:

public void DoStuff(Foo foo)
{
    if (foo == null) return;

    ...
}

तो हाँ, मुझे लगता है कि एक फ़ंक्शन / विधि से कई "निकास बिंदु" होना ठीक है।


83
माना। यद्यपि कई निकास बिंदु हाथ से बाहर निकल सकते हैं, मुझे निश्चित रूप से लगता है कि यह एक IF ब्लॉक में आपके पूरे फ़ंक्शन को डालने से बेहतर है। वापसी का उपयोग करें जितनी बार यह आपके कोड को पठनीय रखने के लिए समझ में आता है।
जोशुआ कारमोडी

172
यह एक "गार्ड स्टेटमेंट" के रूप में जाना जाता है, फाउलर की रिफैक्टिंग है।
लार्स वेस्टरग्रेन ने

12
जब फ़ंक्शंस को अपेक्षाकृत कम रखा जाता है, तो बीच के रिटर्न पॉइंट वाले फ़ंक्शन की संरचना का पालन करना मुश्किल नहीं होता है।
KJAWolf

21
अगर-और बयानों के विशाल खंड, प्रत्येक एक वापसी के साथ? यह कुछ भी नहीं है। ऐसी बात आमतौर पर आसानी से परिलक्षित होती है। (परिणाम चर के साथ अधिक सामान्य एकल निकास भिन्नता है, इसलिए मुझे संदेह है कि बहु निकास विविधता किसी भी अधिक कठिन होगी।) यदि आप एक वास्तविक सिरदर्द चाहते हैं, तो-और-जबकि लूप्स के संयोजन को देखें (स्थानीय द्वारा नियंत्रित) बूलियन), जहां परिणाम चर निर्धारित किए जाते हैं, जिससे विधि के अंत में अंतिम निकास बिंदु बन जाता है। यह एकल निकास विचार पागल हो गया है, और हाँ मैं वास्तविक अनुभव से बात कर रहा हूं जिसके साथ इसे निपटना था।
मार्कस एंड्रेन

7
'कल्पना करें: आपको' DoStuff 'फंक्शन के अंत में "IncreaseStuffCallCounter" विधि करने की आवश्यकता है। इस मामले में आप क्या करेंगे? :) '-DoStuff() { DoStuffInner(); IncreaseStuffCallCounter(); }
जिम बेल्टर

355

किसी ने कोड कम्प्लीट का उल्लेख या उद्धृत नहीं किया है इसलिए मैं इसे करूँगा।

17.1 वापसी

प्रत्येक दिनचर्या में रिटर्न की संख्या कम से कम करें । एक दिनचर्या को समझना कठिन है अगर, इसे नीचे पढ़ रहे हैं, तो आप इस संभावना से अनजान हैं कि यह कहीं ऊपर लौट आया है।

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


64
"कम से कम" की बारीकियों के लिए +1 लेकिन कई रिटर्न पर रोक नहीं।
राएडवल्ड

13
"समझने में कठिन" बहुत व्यक्तिपरक है, खासकर जब लेखक सामान्य दावे का समर्थन करने के लिए कोई अनुभवजन्य साक्ष्य प्रदान नहीं करता है ... एक एकल चर है जिसे अंतिम रिटर्न स्टेटमेंट के लिए कोड में कई सशर्त स्थानों पर सेट किया जाना चाहिए समान रूप से अधीन है "इस संभावना से अनभिज्ञ कि चर को अंतर समारोह के ऊपर कहीं सौंपा गया था!
हेस्टन टी। होल्टमैन

26
निजी तौर पर, मैं जहां संभव हो जल्दी लौटने का पक्ष लेता हूं। क्यों? ठीक है, जब आप किसी दिए गए मामले के लिए रिटर्न कीवर्ड देखते हैं, तो आप तुरंत "मैं समाप्त हो गया" जानता हूं - आपको यह जानने के लिए पढ़ने की ज़रूरत नहीं है कि क्या, अगर कुछ भी, बाद में होता है।
मार्क सिम्पसन

12
@ HestonT.Holtmann: बात यह है कि किए गए कोड को पूरा करें प्रोग्रामिंग पुस्तकों के बीच में अद्वितीय है कि सलाह थी है अनुभवजन्य साक्ष्य द्वारा समर्थित।
एड्रियन मैक्कार्थी

9
यह संभवतः स्वीकृत उत्तर होना चाहिए, क्योंकि इसमें उल्लेख किया गया है कि कई रिटर्न अंक हमेशा अच्छे नहीं होते हैं, फिर भी कभी-कभी आवश्यक होते हैं।
रफीद

229

मैं कहूंगा कि कई निकास बिंदुओं के खिलाफ मनमाने ढंग से निर्णय लेना अविश्वसनीय रूप से नासमझी होगी क्योंकि मैंने तकनीक को बार-बार व्यवहार में उपयोगी पाया है , वास्तव में मैंने स्पष्टता के लिए कई बार मौजूदा निकास कोड को कई निकास बिंदुओं पर रिफैक्ट किया है। हम दो दृष्टिकोणों की तुलना इस प्रकार कर सकते हैं: -

string fooBar(string s, int? i) {
  string ret = "";
  if(!string.IsNullOrEmpty(s) && i != null) {
    var res = someFunction(s, i);

    bool passed = true;
    foreach(var r in res) {
      if(!r.Passed) {
        passed = false;
        break;
      }
    }

    if(passed) {
      // Rest of code...
    }
  }

  return ret;
}

इसकी तुलना उस कोड से करें जहाँ कई निकास बिंदुओं की अनुमति है: -

string fooBar(string s, int? i) {
  var ret = "";
  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

मुझे लगता है कि उत्तरार्द्ध काफी स्पष्ट है। जहां तक ​​मैं बता सकता हूं कि कई निकास बिंदुओं की आलोचना इन दिनों देखने का एक अधिक महत्वपूर्ण बिंदु है।


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

7
सबसे पहले, यह आकस्मिक उदाहरण है। दूसरा, कहां करता है: स्ट्रिंग रिट? "दूसरे संस्करण में जाते हैं? तीसरा, रिट में कोई उपयोगी जानकारी नहीं है। चौथा, एक फ़ंक्शन / पद्धति में इतना तर्क क्यों? पांचवां, DidValuesPass (प्रकार res) क्यों नहीं बनाते हैं तो RestOCode () अलग। उपसंहार?
रिक मिनरिक

25
@ रिक 1. मेरे अनुभव से वंचित नहीं, यह वास्तव में एक पैटर्न है जो मैंने कई बार देखा है, 2. यह 'बाकी कोड' में निर्दिष्ट है, शायद यह स्पष्ट नहीं है। 3. उम? इसका उदाहरण है? 4. वैसे मुझे लगता है कि इस संबंध में
विरोधाभास है

5
@ बात यह है कि एक बड़े बयान में कोड को लपेटने की तुलना में जल्दी लौटना आसान है। यह मेरे अनुभव में बहुत कुछ आता है, यहां तक ​​कि उचित रीफैक्टरिंग के साथ भी।
ljs

5
मल्टीपल रिटर्न स्टेटमेंट न होने की बात डिबगिंग को आसान बना देती है, दूसरी बात यह पठनीयता के लिए। अंत में एक ब्रेकपॉइंट आपको निकास मान, बिना अपवादों को देखने की अनुमति देता है। पठनीयता के लिए 2 दिखाए गए कार्य समान व्यवहार नहीं करते हैं। डिफ़ॉल्ट रिटर्न एक खाली स्ट्रिंग है अगर r! Rassed लेकिन "अधिक पठनीय" एक इसे शून्य वापस करने के लिए बदलता है। लेखक ने गलत संकेत दिया कि पहले केवल कुछ लाइनों के बाद एक डिफ़ॉल्ट था। तुच्छ उदाहरणों में भी स्पष्ट रूप से डिफ़ॉल्ट रिटर्न प्राप्त करना आसान है, अंत में एक एकल रिटर्न लागू करने में मदद करता है।
मिच

191

मैं वर्तमान में एक कोडबेस पर काम कर रहा हूं, जहां पर काम करने वाले दो लोग नेत्रहीन रूप से "सिंगल पॉइंट ऑफ एग्जिट" सिद्धांत की सदस्यता लेते हैं और मैं आपको बता सकता हूं कि अनुभव से, यह एक भयानक भयानक अभ्यास है। यह बनाए रखने के लिए कोड को बहुत कठिन बनाता है और मैं आपको दिखाता हूं कि क्यों।

"निकास के एकल बिंदु" सिद्धांत के साथ, आप अनिवार्य रूप से इस तरह दिखने वाले कोड के साथ हवा देते हैं:

function()
{
    HRESULT error = S_OK;

    if(SUCCEEDED(Operation1()))
    {
        if(SUCCEEDED(Operation2()))
        {
            if(SUCCEEDED(Operation3()))
            {
                if(SUCCEEDED(Operation4()))
                {
                }
                else
                {
                    error = OPERATION4FAILED;
                }
            }
            else
            {
                error = OPERATION3FAILED;
            }
        }
        else
        {
            error = OPERATION2FAILED;
        }
    }
    else
    {
        error = OPERATION1FAILED;
    }

    return error;
}

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

यह विधि कोड रखरखाव को बहुत कठिन और त्रुटि प्रवण बनाती है।


5
@ मर्फ़: आपके पास यह जानने का कोई तरीका नहीं है कि प्रत्येक शर्त के बिना प्रत्येक के माध्यम से पढ़ने के बाद और कुछ नहीं होता है। आम तौर पर मैं कहूंगा कि इस प्रकार के विषय व्यक्तिपरक हैं, लेकिन यह केवल स्पष्ट रूप से गलत है। प्रत्येक त्रुटि पर वापसी के साथ, आप कर रहे हैं, आपको पता है कि वास्तव में क्या हुआ था।
GEOCHET

6
@ मर्फ़: मैंने इस तरह के कोड का इस्तेमाल, गाली-गलौच और अत्याचार देखा है। उदाहरण काफी सरल है, क्योंकि कोई सच नहीं है अगर / अंदर है। सभी कोड के इस प्रकार को तोड़ने की जरूरत है कि एक "और" भूल है। AFAIK, यह कोड अपवादों की वास्तविक बुरी आवश्यकता में है।
पियरसबल

15
आप इसे इसमें शामिल कर सकते हैं, इसकी "शुद्धता" बनाए रखता है: यदि ((SUCCEEDED (Operation1 ())) {} और त्रुटि = OPERATION1FAILED; अगर (त्रुटि = S_OK!) {if (SUCCEEDED (Operation2 ())) {} किसी और त्रुटि = OPERATION2FAILED; } if (त्रुटि! = S_OK) {if (SUCCEEDED (ऑपरेशन 3 ())) {} और त्रुटि = OPERATION3FAILED; } //आदि।
जो पिनेडा

6
इस कोड में सिर्फ एक निकास बिंदु नहीं है: उन "त्रुटि =" बयानों में से प्रत्येक एक निकास पथ पर है। यह केवल बाहर निकलने के कार्यों के बारे में नहीं है, यह किसी भी ब्लॉक या अनुक्रम से बाहर निकलने के बारे में है।
जे बाज़ुज़ी

6
मैं असहमत हूं कि "रिटर्न" अनिवार्य रूप से गहरे घोंसले के शिकार हैं। आपका उदाहरण एक भी वापसी (gotos के बिना) के साथ एक सरल, रैखिक समारोह के रूप में लिखा जा सकता है। और यदि आप संसाधन प्रबंधन के लिए विशेष रूप से RAII पर भरोसा नहीं कर सकते हैं या नहीं कर सकते हैं, तो जल्दी रिटर्न लीक या डुप्लिकेट कोड का कारण बनता है। सबसे महत्वपूर्ण बात, शुरुआती रिटर्न इसे पोस्ट-कंडीशंस पर जोर देने के लिए अव्यवहारिक बनाते हैं।
एड्रियन मैक्कार्थी

72

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


44
संरचित प्रोग्रामिंग एक बात नहीं कहती है। कुछ (लेकिन सभी नहीं) जो लोग खुद को संरचित प्रोग्रामिंग के वकील के रूप में वर्णित करते हैं, वे कहते हैं।
1

15
"यह अच्छी तरह से काम करता है यदि आप उसकी अन्य सलाह का पालन करते हैं और छोटे कार्य लिखते हैं।" यह सबसे महत्वपूर्ण बिंदु है। छोटे कार्यों को शायद ही कभी कई वापसी अंक की जरूरत है।

6
@wnoise +1 टिप्पणी के लिए, यह सच है। सभी "स्ट्रक्चरल प्रोग्रामिंग" कहते हैं कि गोटो का उपयोग न करें।
paxos1977

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

1
जी नहीं, इसका उल्टा - "सभी कोड की 'संरचना' अनदेखी के बारे में कर रहे हैं।"। "इससे समझ में आता है कि उन्हें टाला जाना चाहिए" - नहीं, ऐसा नहीं है।
जिम बाल्टर

62

केंट बेक नोट के रूप में जब कार्यान्वयन पैटर्न में गार्ड क्लॉस पर चर्चा करते हुए एक रूटीन में एक ही प्रविष्टि और निकास बिंदु होता है ...

"एक ही दिनचर्या में कई स्थानों पर और बाहर कूदते समय भ्रम को रोकना संभव था। यह बहुत अच्छा समझ में आता है जब कई वैश्विक आंकड़ों के साथ लिखे गए फोरट्रान या असेंबली भाषा के कार्यक्रमों पर लागू किया जाता है, यहां तक ​​कि यह भी समझना कि किन बयानों को निष्पादित किया गया था, कड़ी मेहनत थी। .. , छोटे तरीकों और ज्यादातर स्थानीय आंकड़ों के साथ, यह अनावश्यक रूप से रूढ़िवादी है। "

मुझे पता चलता है कि गार्ड के साथ लिखा गया एक फ़ंक्शन, if then elseबयानों के एक लंबे नेस्टेड गुच्छा का पालन करने के लिए बहुत आसान है ।


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

@AdrianMcCarthy आपके पास एक बेहतर विकल्प है? यह व्यंग्य की तुलना में अधिक उपयोगी होगा।
Shinzou

@ कुक्कू: मुझे यकीन नहीं है कि मैं उस व्यंग्य को कहूंगा। जवाब से पता चलता है कि यह या तो / या स्थिति है: या तो गार्ड क्लॉस या लंबे समय से नेस्टेड गुच्छों की अगर-फिर-अन्यथा। कई (सबसे?) प्रोग्रामिंग भाषा गार्ड क्लॉज के अलावा ऐसे तर्क को कारक बनाने के कई तरीके प्रदान करती हैं।
एड्रियन मैक्कार्थी

61

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

दूसरे शब्दों में, जब संभव हो, इस शैली का पक्ष लें

return a > 0 ?
  positively(a):
  negatively(a);

इस पर

if (a > 0)
  return positively(a);
else
  return negatively(a);

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

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


6
+1 "यदि आप पाते हैं कि आपके ifs और elses वाक्यात्मक रूप से अलग हैं, तो आप इसे छोटे कार्यों में तोड़ सकते हैं।"
एंड्रेस जान टैक

4
+1, यदि यह एक समस्या है, तो आमतौर पर इसका मतलब है कि आप किसी एक फ़ंक्शन में बहुत अधिक काम कर रहे हैं। यह वास्तव में मुझे उदास करता है कि यह सबसे ज्यादा वोट दिया जाने वाला उत्तर नहीं है
मैट ब्रिग्स

1
गार्ड के बयानों का कोई दुष्प्रभाव नहीं है, लेकिन ज्यादातर लोग उन्हें उपयोगी मानते हैं। इसलिए कोई साइड इफेक्ट न होने पर भी जल्‍दी से जल्‍दी रोक लगाने के कारण हो सकते हैं। यह जवाब पूरी तरह से मेरी राय में मुद्दे को संबोधित नहीं करता है।
मार्टन बोदवेस

@ MaartenBodewes-owlstead देखें "सख़्ती और आलस्य"
Apocalisp

43

मैंने इसे C ++ के लिए कोडिंग मानकों में देखा है जो C से हैंग-ओवर थे, जैसे अगर आपके पास RAII या अन्य स्वचालित मेमोरी प्रबंधन नहीं है, तो आपको प्रत्येक रिटर्न के लिए सफाई करनी होगी, जिसका मतलब है कि कट-एंड-पेस्ट क्लीन-अप या एक गोटो (तार्किक रूप से प्रबंधित भाषाओं में 'अंत में' के समान), दोनों को ही खराब रूप माना जाता है। यदि आपकी प्रथाओं में C ++ या किसी अन्य स्वचालित मेमोरी सिस्टम में स्मार्ट पॉइंटर्स और संग्रह का उपयोग करना है, तो इसके लिए एक मजबूत कारण नहीं है, और यह पठनीयता, और एक निर्णय कॉल के बारे में अधिक हो जाता है।


अच्छी तरह से कहा जाता है, हालांकि मुझे विश्वास है कि अत्यधिक अनुकूलित कोड (जैसे सॉफ्टवेयर स्किनिंग कॉम्प्लेक्स 3 डी मेशेस) लिखने की कोशिश करने पर डिलीट को कॉपी करना बेहतर है
ग्रांट पीटर्स

1
आप किस कारण से विश्वास करते हैं? यदि आपके पास खराब अनुकूलन के साथ एक कंपाइलर है, जहां डीरेफेरेंसिंग में कुछ ओवरहेड है auto_ptr, तो आप समानांतर में सादे पॉइंटर्स का उपयोग कर सकते हैं। हालाँकि पहले स्थान पर गैर-अनुकूलन वाले संकलक के साथ 'अनुकूलित' कोड लिखना अजीब होगा।
पीट किर्कम

यह नियम के लिए एक दिलचस्प अपवाद के लिए बनाता है: यदि आपकी प्रोग्रामिंग भाषा में ऐसा कुछ नहीं है जो स्वचालित रूप से विधि के अंत में कहा जाता है (जैसे कि जावा में try... finally) और आपको संसाधन रखरखाव करने की आवश्यकता है जो आप एक के साथ कर सकते हैं एक विधि के अंत में लौटें। इससे पहले कि आप ऐसा करें, आपको स्थिति से छुटकारा पाने के लिए कोड को फिर से भरने पर गंभीरता से विचार करना चाहिए।
मार्टन बोडेवेस

@PeteKirkham सफाई के लिए गोटो क्यों खराब है? हां गोटो को बुरी तरह से इस्तेमाल किया जा सकता है, लेकिन यह विशेष उपयोग बुरा नहीं है।
q126y

1
CII में @ q126y, RAII के विपरीत, जब कोई अपवाद फेंका जाता है तो वह विफल हो जाता है। सी में, यह पूरी तरह से मान्य अभ्यास है। देखें stackoverflow.com/questions/379172/use-goto-or-not
पीट किर्कम

40

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


38

क्या ऐसे अच्छे कारण हैं कि किसी फ़ंक्शन में केवल एक रिटर्न स्टेटमेंट रखना बेहतर अभ्यास है?

हाँ , वहाँ हैं:

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

प्रश्न अक्सर कई रिटर्न के बीच एक गलत द्वंद्वात्मकता के रूप में प्रस्तुत किया जाता है या यदि बयान में गहराई से घोंसला होता है। लगभग हमेशा एक तीसरा समाधान होता है जो केवल एक ही निकास बिंदु के साथ बहुत रैखिक (कोई गहरी घोंसले का शिकार) नहीं है।

अद्यतन : जाहिर तौर पर MISRA दिशानिर्देश एकल निकास को भी बढ़ावा देते हैं।

स्पष्ट होने के लिए, मैं यह नहीं कह रहा हूं कि कई रिटर्न रखना हमेशा गलत है। लेकिन अन्यथा समकक्ष समाधान दिए गए हैं, एक ही रिटर्न के साथ एक को पसंद करने के बहुत सारे अच्छे कारण हैं।


2
एक और अच्छा कारण, शायद इन दिनों सबसे अच्छा है, एक एकल वापसी बयान लॉगिंग है। यदि आप किसी विधि में लॉगिंग जोड़ना चाहते हैं, तो आप एक एकल लॉग स्टेटमेंट रख सकते हैं जो बताता है कि विधि क्या लौटाती है।
inor

FORTRAN ENTRY का कथन कितना सामान्य था? Docs.oracle.com/cd/E19957-01/805-4939/6j4m0vn99/index.html देखें । और अगर आप साहसी हैं, तो आप AOP और सलाह के बाद तरीके से लॉग इन कर सकते हैं
Eric Jablow

1
+1 पहले 2 अंक मुझे समझाने के लिए पर्याप्त थे। वह दूसरे अंतिम पैराग्राफ के साथ। मैं लॉगिंग तत्व के साथ उसी कारण से असहमत हूं क्योंकि मैं गहरी नेस्टेड स्थिति को हतोत्साहित करता हूं क्योंकि वे एकल जिम्मेदारी नियम को तोड़ने के लिए प्रोत्साहित करते हैं जो मुख्य कारण बहुरूपता को ओओपी में पेश किया गया था।
फ्रांसिस रॉजर्स

मैं सिर्फ C # और कोड कॉन्ट्रैक्ट्स के साथ जोड़ना चाहूंगा, पोस्ट-कंडीशन की समस्या एक गैर-कारण है, क्योंकि आप अभी भी Contract.Ensuresकई रिटर्न पॉइंट्स के साथ उपयोग कर सकते हैं ।
13

1
@ q126y: यदि आप gotoकॉमन क्लीनअप कोड प्राप्त करने के लिए उपयोग कर रहे हैं , तो आपने संभवतः फ़ंक्शन को सरल कर दिया है, ताकि returnक्लीन-अप कोड के अंत में एकल हो। तो आप कह सकते हैं कि आपने समस्या को हल कर लिया है goto, लेकिन मैं कहूंगा कि आपने इसे सरल बनाकर इसे हल कर दिया है return
एड्रियन मैक्कार्थी

33

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


6
वाहवाही! इस उद्देश्य का उल्लेख करने वाले आप एकमात्र व्यक्ति हैं । यही कारण है कि मैं एकल निकास बिंदु बनाम एकाधिक निकास बिंदु पसंद करता हूं। अगर मेरा डिबगर किसी भी निकास बिंदु पर एक विराम बिंदु सेट कर सकता है, तो मैं संभवतः कई निकास बिंदु पसंद करूंगा। मेरी वर्तमान राय उन लोगों की है जो कई एक्ज़िट पॉइंट्स को कोड करते हैं, यह उन लोगों की कीमत पर अपने स्वयं के लाभ के लिए करते हैं जिन्हें अपने कोड पर डिबगर का उपयोग करना पड़ता है (और हाँ मैं आप सभी ओपन-सोर्स योगदानकर्ताओं के बारे में बात कर रहा हूं जो कोड लिखते हैं कई निकास बिंदु।)
माइकस्किंकल

3
हाँ। मैं एक सिस्टम में लॉगिंग कोड जोड़ रहा हूं जो उत्पादन में लगातार दुर्व्यवहार कर रहा है (जहां मैं इसके माध्यम से कदम नहीं उठा सकता)। यदि पिछले कोडर ने एकल-निकास का उपयोग किया था, तो यह बहुत आसान होता।
माइकल ब्लैकबर्न

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

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

7
मैंने लंबे समय तक एक डिबगर नहीं देखा है, जो आपको विधि के "करीब" (अंत, सही घुंघराले ब्रेस, जो भी आपकी भाषा है) पर एक ब्रेकपॉइंट सेट करने की अनुमति नहीं देगा और उस ब्रेकपॉइंट को हिट करें जहां परवाह किए बिना, या कितने , रिटर्न स्टेटमेट विधि में हैं। इसके अलावा, भले ही आप फ़ंक्शन का केवल एक ही रिटर्न है, इसका मतलब यह नहीं है कि आप अपवाद (स्पष्ट या विरासत में) के साथ फ़ंक्शन से बाहर नहीं निकल सकते। इसलिए, मुझे लगता है कि यह वास्तव में एक वैध बिंदु नहीं है।
स्कॉट गार्टनर

19

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


"सामान्य तौर पर मैं एक फ़ंक्शन से केवल एक ही निकास बिंदु के लिए प्रयास करता हूं" - क्यों? "लक्ष्य संभव के रूप में कुछ निकास बिंदु होना चाहिए" - क्यों? और 19 लोगों ने इस गैर-जवाब को वोट क्यों दिया?
जिम बेल्टर

@JimBalter अंततः, यह व्यक्तिगत प्राथमिकता के लिए उबालता है। अधिक निकास बिंदु आम तौर पर एक अधिक जटिल विधि का नेतृत्व करते हैं (हालांकि हमेशा नहीं) और किसी को समझने में अधिक कठिन बनाते हैं।
स्कॉट डोरमैन

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

14

नहीं, क्योंकि हम 1970 के दशक में नहीं रहते हैं । यदि आपका कार्य लंबा है, तो कई रिटर्न एक समस्या है, यह बहुत लंबा है।

(इस तथ्य से अलग कि अपवाद वाली भाषा में किसी भी मल्टी-लाइन फ़ंक्शन के पास वैसे भी कई निकास बिंदु होंगे।)


14

मेरी प्राथमिकता एकल निकास के लिए होगी जब तक कि यह वास्तव में चीजों को जटिल न करे। मैंने पाया है कि कुछ मामलों में, कई मौजूद बिंदु अन्य महत्वपूर्ण डिजाइन समस्याओं का सामना कर सकते हैं:

public void DoStuff(Foo foo)
{
    if (foo == null) return;
}

इस कोड को देखने पर, मैं तुरंत पूछूंगा:

  • क्या 'फू' कभी अशक्त होता है?
  • यदि हां, तो 'DoStuff' के कितने ग्राहक कभी-कभी एक अशक्त 'फू' के साथ फ़ंक्शन को कॉल करते हैं?

इन सवालों के जवाबों के आधार पर ऐसा हो सकता है

  1. जाँच व्यर्थ है क्योंकि यह कभी सत्य नहीं है (अर्थात यह एक मुखर होना चाहिए)
  2. चेक बहुत कम सच है और इसलिए उन विशिष्ट कॉलर कार्यों को बदलने के लिए बेहतर हो सकता है क्योंकि उन्हें शायद वैसे भी कुछ अन्य कार्रवाई करनी चाहिए।

उपरोक्त दोनों मामलों में यह सुनिश्चित करने के लिए कि शायद 'फू ’कभी अशक्त न हो और संबंधित कॉलर्स बदल गया हो, कोड को संभवतः एक जोर के साथ फिर से काम किया जा सकता है।

दो अन्य कारण हैं (विशिष्ट मुझे लगता है कि सी ++ कोड) जहां कई मौजूद हैं वास्तव में नकारात्मक प्रभाव पड़ सकता है। वे कोड आकार, और संकलक अनुकूलन हैं।

किसी गैर-पीओडी C ++ ऑब्जेक्ट को किसी फ़ंक्शन के बाहर निकलने के दायरे में इसके विध्वंसक कहा जाएगा। जहां कई रिटर्न स्टेटमेंट्स हैं, ऐसा हो सकता है कि स्कोप में अलग-अलग ऑब्जेक्ट्स हों और इसलिए कॉल करने के लिए डिस्ट्रक्टर्स की लिस्ट अलग होगी। इसलिए कंपाइलर को प्रत्येक रिटर्न स्टेटमेंट के लिए कोड जनरेट करना होगा:

void foo (int i, int j) {
  A a;
  if (i > 0) {
     B b;
     return ;   // Call dtor for 'b' followed by 'a'
  }
  if (i == j) {
     C c;
     B b;
     return ;   // Call dtor for 'b', 'c' and then 'a'
  }
  return 'a'    // Call dtor for 'a'
}

यदि कोड का आकार एक मुद्दा है - तो यह बचने लायक कुछ हो सकता है।

अन्य मुद्दा "नामांकित रिटर्न वैल्यू ऑप्टिमाइज़ेशन" (उर्फ कॉपी एलीशन, आईएसओ सी ++ +03 12.8 / 15) से संबंधित है। C ++ कॉपी कंस्ट्रक्टर को कॉल करने के लिए कार्यान्वयन को कार्यान्वित करने की अनुमति देता है यदि यह हो सकता है:

A foo () {
  A a1;
  // do something
  return a1;
}

void bar () {
  A a2 ( foo() );
}

कोड को बस के रूप में लेते हुए, 'a1' का निर्माण 'foo' में किया जाता है और फिर इसकी कॉपी निर्माण को 'a2' बनाने के लिए बुलाया जाएगा। हालाँकि, कॉपी एलीजन कंपाइलर को 'a2' के समान स्टैक पर 'a1' के निर्माण की अनुमति देता है। इसलिए फ़ंक्शन के वापस आने पर ऑब्जेक्ट को "कॉपी" करने की कोई आवश्यकता नहीं है।

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


1
यदि आप अपने C ++ के उदाहरण से सभी को हटाते हैं, लेकिन B और बाद में C और B को नष्ट करने के लिए कोड, तब भी उत्पन्न करना होगा जब if स्टेटमेंट का दायरा समाप्त हो जाता है, तो आप वास्तव में एकाधिक रिटर्न नहीं होने से कुछ भी हासिल नहीं करते हैं ।
ग्रहण

4
+1 और वायाए की सूची के निचले भाग में हमारे पास वास्तविक कारण है जिससे यह कोडिंग अभ्यास मौजूद है - NRVO। हालाँकि, यह एक माइक्रो-ऑप्टिमाइज़ेशन है; और, सभी माइक्रो-ऑप्टिमाइज़ेशन प्रथाओं की तरह, शायद 50 वर्षीय "विशेषज्ञ" द्वारा शुरू किया गया था, जो 300 kHz पीडीपी -8 पर प्रोग्रामिंग करने के लिए उपयोग किया जाता है, और स्वच्छ और संरचित कोड के महत्व को नहीं समझता है। सामान्य तौर पर, क्रिस एस की सलाह लें और जब भी इसका कोई मतलब हो तो कई रिटर्न स्टेटमेंट का उपयोग करें।
ब्लूराजा - डैनी पफ्लुगुएफ्ट

जबकि मैं आपकी प्राथमिकता (मेरे दिमाग में, आपके मुखर सुझाव भी एक वापसी बिंदु है, जैसा कि throw new ArgumentNullException()इस मामले में C # में है) से असहमत हूं , मुझे वास्तव में आपके अन्य विचार पसंद हैं, वे सभी मेरे लिए मान्य हैं, और कुछ में महत्वपूर्ण हो सकते हैं आला संदर्भ।
जुलीगॉन

यह स्ट्रोमैन से भरपूर है। क्यों fooपरीक्षण किया जा रहा है का सवाल विषय से कोई लेना-देना नहीं है, जो करना है if (foo == NULL) return; dowork; याif (foo != NULL) { dowork; }
जिम बॅटर

11

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


बहुत ही खुशनुमा। हालाँकि, मुझे लगता है कि जब तक एक प्रोग्रामर को पता नहीं है कि कई एक्ज़िट पॉइंट्स का उपयोग कब करना है, उन्हें एक के लिए विवश होना चाहिए।
रिक मिन्रिच

5
ज़रुरी नहीं। "यदि (...) वापसी? ... वापसी की चक्रीय जटिलता?" "अगर (...) {...} वापसी" के समान है। उनके माध्यम से दोनों के पास दो रास्ते हैं।
स्टीव एम्मर्सन

11

मैं खुद को केवल एक returnबयान का उपयोग करने के लिए मजबूर करता हूं , क्योंकि यह एक अर्थ में कोड गंध पैदा करेगा। मुझे समझाने दो:

function isCorrect($param1, $param2, $param3) {
    $toret = false;
    if ($param1 != $param2) {
        if ($param1 == ($param3 * 2)) {
            if ($param2 == ($param3 / 3)) {
                $toret = true;
            } else {
                $error = 'Error 3';
            }
        } else {
            $error = 'Error 2';
        }
    } else {
        $error = 'Error 1';
    }
    return $toret;
}

(स्थितियां अर्हक हैं ...)

जितनी अधिक परिस्थितियां, जितना बड़ा कार्य होता है, पढ़ने में उतना ही कठिन होता है। इसलिए यदि आप कोड गंध से जुड़े हैं, तो आपको इसका एहसास होगा, और कोड को फिर से भरना होगा। दो संभावित समाधान हैं:

  • एकाधिक रिटर्न
  • अलग-अलग कार्यों में रिफैक्टिंग

मल्टीपल रिटर्न

function isCorrect($param1, $param2, $param3) {
    if ($param1 == $param2)       { $error = 'Error 1'; return false; }
    if ($param1 != ($param3 * 2)) { $error = 'Error 2'; return false; }
    if ($param2 != ($param3 / 3)) { $error = 'Error 3'; return false; }
    return true;
}

अलग कार्य

function isEqual($param1, $param2) {
    return $param1 == $param2;
}

function isDouble($param1, $param2) {
    return $param1 == ($param2 * 2);
}

function isThird($param1, $param2) {
    return $param1 == ($param2 / 3);
}

function isCorrect($param1, $param2, $param3) {
    return !isEqual($param1, $param2)
        && isDouble($param1, $param3)
        && isThird($param2, $param3);
}

दी, यह लंबा और थोड़ा गड़बड़ है, लेकिन इस तरह से फ़ंक्शन को फिर से शुरू करने की प्रक्रिया में, हमने

  • पुन: प्रयोज्य कार्यों के एक नंबर बनाया,
  • समारोह को और अधिक मानव पठनीय बनाया, और
  • फ़ंक्शंस का फोकस इस बात पर है कि वैल्यूज़ सही क्यों हैं।

5
-1: खराब उदाहरण। आपने त्रुटि संदेश हैंडलिंग को छोड़ दिया है। अगर जरूरत नहीं है तो सही को xx && yy && zz के रूप में लौटाया जा सकता है; जहाँ xx, yy और z isEqual, isDouble और isThird अभिव्यक्तियाँ हैं।
कौप्पी

10

मैं कहूंगा कि आपके पास आवश्यक रूप से कई होने चाहिए, या कोई भी जो कोड क्लीनर (जैसे गार्ड क्लॉज ) बनाता है ।

मैंने व्यक्तिगत रूप से किसी भी "सर्वोत्तम प्रथाओं" को कभी नहीं सुना / देखा है कि आप केवल एक रिटर्न स्टेटमेंट होना चाहिए।

अधिकांश भाग के लिए, मैं एक तर्क पथ के आधार पर जल्द से जल्द एक समारोह से बाहर निकलता हूं (गार्ड क्लॉज इस का एक उत्कृष्ट उदाहरण हैं)।


10

मेरा मानना ​​है कि आमतौर पर कई रिटर्न अच्छे होते हैं (कोड में जो मैं सी # में लिखता हूं)। एकल-वापसी शैली सी से होल्डओवर है लेकिन आप शायद सी में कोडिंग नहीं कर रहे हैं।

सभी प्रोग्रामिंग भाषाओं में एक विधि के लिए केवल एक निकास बिंदु की आवश्यकता वाला कोई कानून नहीं है । कुछ लोग इस शैली की श्रेष्ठता पर जोर देते हैं, और कभी-कभी वे इसे "नियम" या "कानून" तक बढ़ाते हैं, लेकिन यह विश्वास किसी भी सबूत या शोध से समर्थित नहीं है।

C कोड में एक से अधिक रिटर्न स्टाइल एक बुरी आदत हो सकती है, जहाँ संसाधनों को स्पष्ट रूप से आबंटित किया जाना है, लेकिन जावा, C #, पायथन या जावास्क्रिप्ट जैसी भाषाएं जिनमें स्वत: कचरा संग्रह और try..finallyब्लॉक ( usingC # में ब्लॉक) जैसे निर्माण हैं ), और यह तर्क लागू नहीं होता है - इन भाषाओं में, केंद्रीकृत मैनुअल संसाधन डीलोकेशन की आवश्यकता होना बहुत ही असामान्य है।

ऐसे मामले हैं जहां एक एकल वापसी अधिक पठनीय है, और ऐसे मामले जहां यह नहीं है। देखें कि क्या यह कोड की लाइनों की संख्या को कम करता है, तर्क को स्पष्ट करता है या ब्रेसिज़ और इंडेंट या अस्थायी चर की संख्या को कम करता है।

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

मैंने इस बारे में अपने ब्लॉग पर अधिक लंबाई में बात की है ।


10

एकल निकास बिंदु के बारे में कहने के लिए अच्छी चीजें हैं, जैसे कि अपरिहार्य "तीर" प्रोग्रामिंग के बारे में कहने के लिए खराब चीजें हैं जो परिणाम देती हैं।

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

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

यदि आप वास्तव में एक एकल स्थान में संसाधनों को जारी करने के लिए उदाहरण के लिए एक एकल निकास बिंदु (किसी भी गैर-अपवाद-सक्षम भाषा में) चाहते हैं, तो मुझे अच्छा होने के लिए गोटो का सावधानीपूर्वक आवेदन मिलता है; उदाहरण के लिए इसे देखें नहीं बल्कि उदाहरण के लिए (स्क्रीन रियल एस्टेट को बचाने के लिए संकुचित):

int f(int y) {
    int value = -1;
    void *data = NULL;

    if (y < 0)
        goto clean;

    if ((data = malloc(123)) == NULL)
        goto clean;

    /* More code */

    value = 1;
clean:
   free(data);
   return value;
}

व्यक्तिगत रूप से I, सामान्य रूप से, मैं एक से अधिक निकास-बिंदुओं को नापसंद करने से अधिक तीर प्रोग्रामिंग को नापसंद करता हूं, हालांकि दोनों सही ढंग से लागू होने पर उपयोगी होते हैं। सबसे अच्छा, निश्चित रूप से, न तो आवश्यकता के लिए अपने कार्यक्रम की संरचना करना है। अपने कार्य को कई विखंडू में तोड़कर आमतौर पर मदद करते हैं :)

हालांकि ऐसा करते समय, मुझे लगता है कि मैं इस उदाहरण में कई एक्ज़िट पॉइंट्स के साथ समाप्त होता हूं, जहां कुछ बड़े फ़ंक्शन को किसी भी छोटे हिस्से में तोड़ दिया गया है:

int g(int y) {
  value = 0;

  if ((value = g0(y, value)) == -1)
    return -1;

  if ((value = g1(y, value)) == -1)
    return -1;

  return g2(y, value);
}

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

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

संक्षेप में;

  • कुछ रिटर्न कई रिटर्न से बेहतर हैं
  • विशाल तीर की तुलना में एक से अधिक वापसी बेहतर है, और गार्ड क्लॉस आमतौर पर ठीक हैं।
  • अपवाद संभव होने पर अधिकांश 'गार्ड क्लॉस' को बदल सकते हैं।

उदाहरण y <0 के लिए क्रैश हो जाता है, क्योंकि यह NULL पॉइंटर को मुक्त करने की कोशिश करता है ;-)
Erich Kitzmueller

2
opengroup.org/onlinepubs/009695399/functions/free.html "यदि ptr एक शून्य सूचक है, तो कोई कार्रवाई नहीं होगी।"
हेनरिक गुस्ताफसन

1
नहीं, यह दुर्घटनाग्रस्त नहीं होगा, क्योंकि NULL को मुफ्त में पास करना एक परिभाषित नो-ऑप है। यह एक कष्टप्रद सामान्य गलत धारणा है जिसे आपको NULL के लिए पहले परखना होगा।
२०'१० को

"तीर" पैटर्न एक अपरिहार्य विकल्प नहीं है। यह एक गलत द्वंद्व है।
एड्रियन मैक्कार्थी

9

आप जानते हैं कि कहावत - सुंदरता देखने वाले की आंखों में है

कुछ लोग NetBeans और कुछ IntelliJ IDEA , कुछ पायथन द्वारा और कुछ PHP द्वारा शपथ लेते हैं ।

कुछ दुकानों में आप अपनी नौकरी खो सकते हैं यदि आप ऐसा करने पर जोर देते हैं:

public void hello()
{
   if (....)
   {
      ....
   }
}

प्रश्न दृश्यता और स्थिरता के बारे में है।

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

अरे दोस्त (जैसे एक पूर्व ग्राहक कहा करते थे) वही करें जो आप चाहते हैं जब तक आप जानते हैं कि इसे कैसे ठीक किया जाए जब मुझे आपको इसे ठीक करने की आवश्यकता होती है।

मुझे याद है कि 20 साल पहले, मेरे एक सहयोगी को रोजगार के लिए निकाल दिया गया था, जिसे आज की फुर्तीली विकास रणनीति कहा जाएगा । उनके पास एक सावधानीपूर्वक वृद्धिशील योजना थी। लेकिन उसका प्रबंधक उस पर चिल्ला रहा था "आप उपयोगकर्ताओं के लिए आकस्मिक रूप से सुविधाएँ जारी नहीं कर सकते! आपको झरने से चिपकना चाहिए ।" प्रबंधक के प्रति उनकी प्रतिक्रिया थी कि वृद्धिशील विकास ग्राहक की जरूरतों के लिए अधिक सटीक होगा। वह ग्राहकों की जरूरतों के लिए विकसित होने में विश्वास करता था, लेकिन प्रबंधक "ग्राहक की आवश्यकता" को कोड करने में विश्वास करता था।

हम डेटा के सामान्यीकरण, एमवीपी और एमवीसी सीमाओं को तोड़ने के लिए अक्सर दोषी होते हैं । हम एक फ़ंक्शन का निर्माण करने के बजाय इनलाइन करते हैं। हम शॉर्टकट लेते हैं।

व्यक्तिगत रूप से, मेरा मानना ​​है कि PHP बुरा अभ्यास है, लेकिन मुझे क्या पता है। सभी सैद्धांतिक तर्क नियमों के एक सेट को पूरा करने की कोशिश करते हैं

गुणवत्ता = परिशुद्धता, रखरखाव और लाभप्रदता।

अन्य सभी नियम पृष्ठभूमि में फीके हैं। और निश्चित रूप से यह नियम कभी नहीं मिटता:

आलस्य एक अच्छे प्रोग्रामर का गुण है।


1
"अरे दोस्त (जैसे एक पूर्व ग्राहक कहता था) वह करो जो तुम चाहते हो कि जब तक तुम्हें पता है कि इसे कैसे ठीक करना है जब मुझे इसे ठीक करना है।" समस्या: यह अक्सर आप नहीं होते हैं जो इसे 'ठीक' करते हैं।
दान बैरन

इस जवाब पर +1 क्योंकि मैं इस बात से सहमत हूं कि आप कहां जा रहे हैं लेकिन जरूरी नहीं कि आप वहां कैसे पहुंचें। मैं तर्क दूंगा कि समझ के स्तर हैं। यानी प्रोग्रामिंग ए के 5 साल और कंपनी के साथ 5 साल बाद कर्मचारी ए की समझ, कर्मचारी बी की तुलना में बहुत अलग है, एक नया कॉलेज जो सिर्फ कंपनी के साथ शुरू होता है। मेरा कहना यह होगा कि यदि कर्मचारी A केवल वे लोग हैं जो कोड को समझ सकते हैं, तो यह रखरखाव योग्य नहीं है और इसलिए हम सभी को कोड लिखने के लिए प्रयास करना चाहिए जिसे कर्मचारी B समझ सकता है। यह वह जगह है जहां सॉफ्टवेयर में असली कला है।
फ्रांसिस रोडर्स

9

मैं जल्दी लौटने के लिए गार्ड क्लॉस का उपयोग करने की ओर झुक गया और अन्यथा एक विधि के अंत में बाहर निकलता हूं। एकल प्रविष्टि और निकास नियम का ऐतिहासिक महत्व है और कई कोड (और कई दोष) के साथ एकल C ++ विधि के लिए 10 A4 पृष्ठों तक चलने वाली विरासत कोड से निपटने में विशेष रूप से सहायक था। हाल ही में, स्वीकार किए जाते हैं अच्छा अभ्यास तरीकों को छोटा रखने के लिए है जो कई निकास को समझने के लिए एक बाधा से कम करता है। ऊपर से कॉपी किए गए क्रोनोज़ उदाहरण में, सवाल यह है कि क्या होता है // बाकी कोड ... ??

void string fooBar(string s, int? i) {

  if(string.IsNullOrEmpty(s) || i == null) return null;

  var res = someFunction(s, i);

  foreach(var r in res) {
      if(!r.Passed) return null;
  }

  // Rest of code...

  return ret;
}

मुझे लगता है कि उदाहरण कुछ हद तक वंचित है, लेकिन मैं एक LINQ बयान में फॉरेस्ट लूप को रिफैक्ट करने के लिए लुभाऊंगा, जिसे तब एक गार्ड क्लॉज माना जा सकता है। फिर, एक आकस्मिक उदाहरण में कोड का आशय स्पष्ट नहीं है और someFunction () के कुछ अन्य दुष्प्रभाव हो सकते हैं या परिणाम का उपयोग // बाकी कोड ... में किया जा सकता है ।

if (string.IsNullOrEmpty(s) || i == null) return null;
if (someFunction(s, i).Any(r => !r.Passed)) return null;

निम्नलिखित रिफैक्टेड फ़ंक्शन देते हुए:

void string fooBar(string s, int? i) {

  if (string.IsNullOrEmpty(s) || i == null) return null;
  if (someFunction(s, i).Any(r => !r.Passed)) return null;

  // Rest of code...

  return ret;
}

क्या C ++ में अपवाद नहीं हैं? फिर आप यह मानने के nullबजाय कि अपवाद को स्वीकार करने के बजाय यह क्यों स्वीकार करेंगे कि तर्क स्वीकार नहीं किया गया है?
Maarten Bodewes

1
जैसा कि मैंने संकेत दिया, उदाहरण कोड एक पिछले उत्तर ( stackoverflow.com/a/36729/132599 ) से कॉपी किया गया है । मूल उदाहरण ने अशक्तता लौटा दी और तर्क को अपवाद के रूप में फेंकना उस बिंदु के लिए महत्वपूर्ण नहीं था जिसे मैं मूल प्रश्न बनाने या करने की कोशिश कर रहा था। एक अच्छे अभ्यास के रूप में, फिर मैं सामान्य रूप से (C # में) एक अशक्त मान वापस करने के बजाय एक गार्ड क्लॉज में एक ArgumentNullException को फेंक दूंगा।
डेविड क्लार्क

7

एक अच्छा कारण मैं सोच सकता हूं कि कोड रखरखाव के लिए: आपके पास निकास का एक बिंदु है। यदि आप परिणाम के प्रारूप को बदलना चाहते हैं, ..., तो इसे लागू करना बहुत सरल है। इसके अलावा, डिबगिंग के लिए, आप वहां एक ब्रेकपॉइंट चिपका सकते हैं :)

यह कहते हुए कि, मुझे एक बार एक पुस्तकालय में काम करना था, जहाँ कोडिंग मानकों ने 'प्रति कार्य एक रिटर्न स्टेटमेंट' लगाया था, और मुझे यह बहुत कठिन लगा। मैं बहुत सारे संख्यात्मक अभिकलन कोड लिखता हूं, और अक्सर 'विशेष मामले' होते हैं, इसलिए कोड का पालन करना काफी कठिन हो गया ...


इससे वास्तव में कोई फर्क नहीं पड़ता। यदि आप लौटाए गए स्थानीय चर के प्रकार को बदलते हैं, तो आपको उस स्थानीय चर को सभी असाइनमेंट को ठीक करना होगा। और किसी भी तरीके से अलग हस्ताक्षर के साथ एक विधि को परिभाषित करना बेहतर है, क्योंकि आपको सभी विधि कॉल को भी ठीक करना होगा।
Maarten Bodewes

@ MaartenBodewes-owlstead - इससे फर्क पड़ सकता है। केवल दो उदाहरणों को नाम देने के लिए, जहां आपको स्थानीय चर के लिए सभी असाइनमेंट्स को ठीक नहीं करना होगा या विधि कॉल को बदलना होगा, फ़ंक्शन एक तारीख को स्ट्रिंग के रूप में वापस कर सकता है (स्थानीय चर एक वास्तविक तारीख होगी, केवल एक स्ट्रिंग के रूप में स्वरूपित अंतिम क्षण), या यह एक दशमलव संख्या वापस कर सकता है और आप दशमलव स्थानों की संख्या को बदलना चाहते हैं।
nnnnnn

@nnnnnn ठीक है, यदि आप आउटपुट पर पोस्ट प्रोसेसिंग करना चाहते हैं ... लेकिन मैं सिर्फ एक नया तरीका उत्पन्न करूंगा जो पोस्ट प्रोसेसिंग करता है और पुराने को अकेला छोड़ देता है। यह केवल रिफैक्टर के लिए थोड़ा कठिन है, लेकिन आपको यह जांचना होगा कि क्या अन्य कॉल वैसे भी नए प्रारूप के साथ संगत हैं। लेकिन यह एक वैध कारण है कम-से-कम।
मार्टन बोदवेस

7

मल्टीपल एग्जिट पॉइंट छोटे पर्याप्त कार्यों के लिए ठीक हैं - अर्थात्, एक फ़ंक्शन जिसे एक स्क्रीन की लंबाई पर पूरी तरह से देखा जा सकता है। यदि इसी तरह एक लंबा कार्य कई निकास बिंदुओं को शामिल करता है, तो यह संकेत है कि फ़ंक्शन को आगे काटा जा सकता है।

कहा कि मैं कई-बाहर निकलने के कार्यों से बचता हूं जब तक कि बिल्कुल आवश्यक न हो । मैंने बगों का दर्द महसूस किया है जो अधिक जटिल कार्यों में कुछ अस्पष्ट रेखा में कुछ आवारा वापसी के कारण हैं।


6

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


उल्लेख करने के लिए अपने मन को बताने के लिए नहीं है ifकि प्रत्येक विधि कॉल के सामने बयान को छोड़ दें जो सफलता लौटाता है या नहीं :(
मैर्टन Bodewes

6

एकल निकास बिंदु - अन्य सभी चीजें समान - कोड को काफी अधिक पठनीय बनाती हैं। लेकिन एक पकड़ है: लोकप्रिय निर्माण

resulttype res;
if if if...
return res;

एक नकली है, "res =" "वापसी" से बहुत बेहतर नहीं है। इसमें सिंगल रिटर्न स्टेटमेंट है, लेकिन कई बिंदु जहां फ़ंक्शन वास्तव में समाप्त होता है।

यदि आपके पास कई रिटर्न (या "res =" s) के साथ कार्य किया जाता है, तो अक्सर एक ही निकास बिंदु के साथ इसे कई छोटे कार्यों में तोड़ने का एक अच्छा विचार है।


6

मेरी सामान्य नीति एक फ़ंक्शन के अंत में केवल एक रिटर्न स्टेटमेंट है जब तक कि कोड की जटिलता बहुत अधिक जोड़कर कम नहीं हो जाती। वास्तव में, मैं बल्कि एफिल का प्रशंसक हूं, जो कि केवल एक रिटर्न नियम लागू करता है, जिसमें कोई रिटर्न स्टेटमेंट नहीं है (अपना रिजल्ट डालने के लिए बस एक ऑटो-रिजल्ट 'चर' है)।

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


5

यदि आप कुछ से अधिक रिटर्न खत्म करते हैं तो आपके कोड में कुछ गड़बड़ हो सकती है। अन्यथा मैं इस बात से सहमत होता हूं कि कभी-कभी यह सबरूटीन में कई स्थानों से लौटने में सक्षम होना अच्छा होता है, खासकर जब यह कोड क्लीनर बनाता है।

पर्ल 6: खराब उदाहरण

sub Int_to_String( Int i ){
  given( i ){
    when 0 { return "zero" }
    when 1 { return "one" }
    when 2 { return "two" }
    when 3 { return "three" }
    when 4 { return "four" }
    ...
    default { return undef }
  }
}

इस तरह लिखा जाना बेहतर होगा

पर्ल 6: अच्छा उदाहरण

@Int_to_String = qw{
  zero
  one
  two
  three
  four
  ...
}
sub Int_to_String( Int i ){
  return undef if i < 0;
  return undef unless i < @Int_to_String.length;
  return @Int_to_String[i]
}

ध्यान दें कि यह सिर्फ एक त्वरित उदाहरण था


ठीक है, यह मतदान क्यों किया गया? इसकी तरह यह एक राय नहीं है।
ब्रैड गिलबर्ट

5

मैं एक दिशानिर्देश के रूप में अंत में सिंगल रिटर्न के लिए वोट करता हूं। यह कॉमन कोड क्लीन-अप हैंडलिंग में मदद करता है ... उदाहरण के लिए, निम्नलिखित कोड पर एक नज़र डालें ...

void ProcessMyFile (char *szFileName)
{
   FILE *fp = NULL;
   char *pbyBuffer = NULL:

   do {

      fp = fopen (szFileName, "r");

      if (NULL == fp) {

         break;
      }

      pbyBuffer = malloc (__SOME__SIZE___);

      if (NULL == pbyBuffer) {

         break;
      }

      /*** Do some processing with file ***/

   } while (0);

   if (pbyBuffer) {

      free (pbyBuffer);
   }

   if (fp) {

      fclose (fp);
   }
}

आप सिंगल रिटर्न के लिए वोट करें - सी कोड में। लेकिन क्या होगा यदि आप एक ऐसी भाषा में कोडिंग कर रहे थे जिसमें कचरा संग्रह था और कोशिश करें..बिना ब्लॉक?
एंथनी

4

यह शायद एक असामान्य परिप्रेक्ष्य है, लेकिन मुझे लगता है कि जो कोई भी मानता है कि कई रिटर्न स्टेटमेंट के पक्षधर हैं, उसे कभी भी माइक्रोप्रोसेसर पर डीबगर का उपयोग नहीं करना पड़ता है जो केवल 4 हार्डवेयर ब्रेकप्वाइंट का समर्थन करता है। ;-)

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


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

आपके डिबगर पर भी निर्भर करता है।
मार्टन बॉड्यूज

4

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

लेकिन, नहीं, एक / कई रिटर्न स्टेटमेंट में कुछ भी गलत नहीं है। कुछ भाषाओं में, यह दूसरों (C) की तुलना में बेहतर अभ्यास (C ++) है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.