मैं जावा पद्धति में बेकार वापसी से कैसे बचूँ?


115

मेरे पास एक ऐसी स्थिति है जहां returnदो forछोरों में निहित बयान हमेशा, सैद्धांतिक रूप से पहुंच जाएगा।

संकलक असहमत है और लूप के returnबाहर एक बयान की आवश्यकता है for। मैं इस तरीके को अनुकूलित करने के लिए एक सुंदर तरीका जानना चाहता हूं जो मेरी वर्तमान समझ से परे है, और मेरे ब्रेक के किसी भी कार्यान्वित कार्यान्वयन से काम नहीं लगता है।

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

private static int oneRun(int range) {
    int[] rInt = new int[range+1]; // Stores the past sequence of ints.
    rInt[0] = generator.nextInt(range); // Inital random number.

    for (int count = 1; count <= range; count++) { // Run until return.
        rInt[count] = generator.nextInt(range); // Add randint to current iteration.
        for (int i = 0; i < count; i++) { // Check for past occurence and return if found.
            if (rInt[i] == rInt[count]) {
                return count;
            }
        }
    }
    return 0; // Never reached
}

9
यदि अंतिम वस्तु कभी नहीं पहुंचती है, तो आप while(true)अनुक्रमित लूप के बजाय उपयोग कर सकते हैं । यह संकलक को बताता है कि लूप कभी वापस नहीं आएगा।
बोरिस द स्पाइडर

101
सीमा के साथ फ़ंक्शन को 0 के रूप में कॉल करें (या किसी भी अन्य संख्या 1 से कम) ( oneRun(0)) और आप देखते हैं कि आप जल्दी से अपने अप्राप्य तक पहुंच जाते हैंreturn
mcfedr

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

4
@HopefullyHelpful वह असली जवाब है, बिल्कुल। यह एक बेकार वापसी नहीं है!
श्री लिस्टर

4
@ हेलोफुल हेल्पफुल नं, क्योंकि एकमात्र nextIntअपवाद के लिए एक अपवाद फेंकता है range < 0जहां वापसी होती हैrange == 0
njzk2

जवाबों:


343

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

private static int oneRun(int range) {
    int[] rInt = new int[range+1]; // Stores the past sequence of ints.
    rInt[0] = generator.nextInt(range); // Inital random number.

    for (int count = 1; count <= range; count++) {
        ...
    }

    throw new AssertionError("unreachable code reached");
}

135
न केवल पठनीयता, बल्कि यह सुनिश्चित करता है कि आपको पता चले कि आपके कोड में कुछ गड़बड़ है या नहीं। यह पूरी तरह से प्रशंसनीय है जनरेटर एक बग हो सकता है।
जॉलीजोकर

6
यदि आप ABSOLUTELY हैं, तो सुनिश्चित करें कि आपका कोड कभी भी उस "पहुंच योग्य कोड" पर नहीं पहुंच सकता है, फिर सभी किनारे के मामलों के लिए कुछ यूनिट परीक्षण बनाएं जो उत्पन्न हो सकते हैं (रेंज 0 है, रेंज -1 है, रेंज न्यूनतम / अधिकतम इंट है)। यह अब एक निजी तरीका हो सकता है, लेकिन अगले डेवलपर इसे इस तरह नहीं रख सकते हैं। आप अनपेक्षित मूल्यों को कैसे संभालते हैं (एक अपवाद को फेंकना, एक त्रुटि मान वापस करना, कुछ भी नहीं करना) इस बात पर निर्भर करता है कि आप विधि का उपयोग कैसे करने जा रहे हैं। मेरे अनुभव में, आप आमतौर पर एक त्रुटि लॉग इन करना चाहते हैं और वापस लौट जाते हैं।
रिक राइकर

22
हो सकता है कि होना चाहिएthrow new AssertionError("\"unreachable\" code reached");
बोहेमियन

8
मैं बिल्कुल वही लिखूंगा जो मैंने अपने जवाब में एक प्रोडक्शन प्रोग्राम में डाला था।
जॉन कुगेलमैन

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

36

जैसा कि @BoristheSpider ने बताया कि आप यह सुनिश्चित कर सकते हैं कि दूसरा returnकथन शब्दार्थ है:

private static int oneRun(int range) {
    int[] rInt = new int[range+1]; // Stores the past sequence of ints.
    int count = 0;

    while (true) {
        rInt[count] = generator.nextInt(range); // Add randint to current iteration.
        for (int i = 0; i < count; i++) { // Check for past occurence and return if found.
            if (rInt[i] == rInt[count]) {
                return count;
            }
        }
        count++;
    }
}

संकलन और ठीक चलता है। और अगर आपको कभी ArrayIndexOutOfBoundsExceptionपता चलता है कि आपको स्पष्ट रूप से कुछ भी फेंकने के बिना, कार्यान्वयन शब्दार्थ में गलत था।


1
बिल्कुल सही: हालत का उपयोग वास्तव में लूप को नियंत्रित करने के लिए नहीं किया जाता है, इसलिए यह नहीं होना चाहिए। हालाँकि, मैं for(int count = 0; true; count++)इसके बजाय यह लिखूंगा ।
विस्फ़ोटक -

3
@cmaster: आप इसे छोड़ सकते हैं true:for(int count = 0; ; count++) …
Holger

@ होल्गर आह। मुझे इस पर यकीन नहीं था क्योंकि यह जावा के बारे में है, और मैंने सालों से उस भाषा को नहीं छुआ है क्योंकि मैं C / C ++ में बहुत अधिक हूं। इसलिए, जब मैंने इसका उपयोग देखा while(true), तो मैंने सोचा कि शायद जावा इस संबंध में थोड़ा अधिक सख्त है। यह जानकर अच्छा लगा कि चिंता करने की कोई आवश्यकता नहीं है ... वास्तव में, मुझे छोड़ा गया trueसंस्करण बहुत अच्छा लगता है: यह स्पष्ट रूप से स्पष्ट करता है कि :-) देखने के लिए बिल्कुल कोई शर्त नहीं है
cmaster - reicaate monica

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

1
@ l0b0 पूरा सवाल शायद कोड की समीक्षा के लिए बेहतर होगा क्योंकि चेतावनी कोड गुणवत्ता से संबंधित है।
सुल्तान

18

चूंकि आपने दो forछोरों को तोड़ने के बारे में पूछा था , आप ऐसा करने के लिए एक लेबल का उपयोग कर सकते हैं (नीचे उदाहरण देखें):

private static int oneRun(int range) {
    int returnValue=-1;

    int[] rInt = new int[range+1]; // Stores the past sequence of ints.
    rInt[0] = generator.nextInt(range); // Inital random number.

    OUTER: for (int count = 1; count <= range; count++) { // Run until return.
        rInt[count] = generator.nextInt(range); // Add randint to current iteration.   
        for (int i = 0; i < count; i++) { // Check for past occurence and return if found.
            if (rInt[i] == rInt[count]) {
                returnValue = count;
                break OUTER;
            }
        }
    }
    return returnValue;
}

क्या यह संकलन में विफल नहीं होगा क्योंकि returnValueइसका उपयोग अनैतिक तरीके से किया जा सकता है?
अंक

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

4
क्या लेबल उस तरह की चीजें नहीं हैं जिनसे आप किसी भी तरह से बचते हैं?
सर्वरफोग

2
मेरा मानना ​​है कि आप गोट्स के बारे में सोच रहे हैं।
डेविड चॉलेर

4
एक उच्च (-ish) स्तर की प्रोग्रामिंग भाषा में लेबल। बिल्कुल बर्बर ...
xDaizu

13

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

Set<Integer> previous = new HashSet<Integer>();

int randomInt = generator.nextInt(range);
previous.add(randomInt);

for (int count = 1; count <= range; count++) {
    randomInt = generator.nextInt(range);
    if (previous.contains(randomInt)) {
       break;
    }

    previous.add(randomInt);
}

return previous.size();

अब ध्यान दें कि हम जो वापसी कर रहे हैं वह वास्तव में सेट का आकार है। कोड की जटिलता द्विघात से रैखिक में कम हो गई है और यह तुरंत अधिक पठनीय है।

अब हम महसूस कर सकते हैं कि हमें उस countसूचकांक की भी आवश्यकता नहीं है :

Set<Integer> previous = new HashSet<Integer>();

int randomInt = generator.nextInt(range);

while (!previous.contains(randomInt)) {          
    previous.add(randomInt);      
    randomInt = generator.nextInt(range);
}

return previous.size();

8

जैसा कि आपका रिटर्न वैल्यू बाहरी लूप के वैरिएबल पर आधारित है, आप count < rangeफ़ंक्शन के अंत में केवल बाहरी लूप की स्थिति को बदल सकते हैं और फिर इस अंतिम मान (जिसे आपने छोड़ दिया है) को वापस कर सकते हैं:

private static int oneRun(int range) {
    ...

    for (int count = 1; count < range; count++) {
        ...
    }
    return range;
}

इस तरह से आपको उस कोड को लागू करने की आवश्यकता नहीं है जो कभी नहीं पहुंचेगा।


लेकिन क्या मैच मिलने पर छोरों की दो परतों को तोड़ने की जरूरत नहीं होगी? मैं बयान के लिए एक को कम करने का एक तरीका नहीं देखता हूं, एक नया यादृच्छिक संख्या उत्पन्न करने की आवश्यकता है और दूसरे की तुलना में पहले यह उत्पन्न होता है।
ओलिवर बेनिंग

आप लूप्स के लिए दो नेस्टेड के साथ अभी भी बचे हुए हैं (मैं अभी दूसरे को छोड़ दिया है ...) लेकिन बाहरी लूप को इसके अंतिम पुनरावृत्ति द्वारा कम कर दिया गया है। इस अंतिम पुनरावृत्ति से संबंधित मामले को बहुत अंतिम रिटर्न स्टेटमेंट द्वारा अलग से संभाला जाता है। यद्यपि यह आपके उदाहरण के लिए एक सुरुचिपूर्ण समाधान है, ऐसे परिदृश्य हैं जिनमें यह दृष्टिकोण पढ़ना मुश्किल हो जाता है; यदि उदाहरण के लिए आंतरिक लूप पर निर्भर रिटर्न मान तो - अंत में सिंगल रिटर्न स्टेटमेंट के बजाय - आपको एक अतिरिक्त लूप (बाहरी लूप के अंतिम पुनरावृत्ति के लिए आंतरिक लूप का प्रतिनिधित्व) के साथ छोड़ दिया जाएगा।
a_guest

5

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


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

@ सोलोमनऑफसर्किट आपका बयान बेतुका है। इसका मतलब है कि हमें दो या अधिक रिटर्न स्टेटमेंट के लिए "सामान्य रूप से" उद्देश्य चाहिए। दिलचस्प।
डेविड

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

@ डेविड मुझे फिर से बताएं: केवल एक रिटर्न होने का कोई सामान्य कारण नहीं है। विशेष रूप से मामलों में कारण हो सकता है, लेकिन आम तौर पर यह सबसे खराब तरीके से कार्गो पंथ है।
मोनिका

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

3

शायद यह एक संकेत है कि आपको अपने कोड को फिर से लिखना चाहिए। उदाहरण के लिए:

  1. पूर्णांक 0 .. श्रेणी -1 की एक सरणी बनाएं। सभी मानों को 0 पर सेट करें।
  2. एक पाश प्रदर्शन करें। लूप में, एक यादृच्छिक संख्या उत्पन्न करें। अपनी सूची में देखें, उस इंडेक्स पर, यह देखने के लिए कि मान 1 है या नहीं, तो लूप से बाहर निकलें। अन्यथा, उस इंडेक्स पर मान को 1 पर सेट करें
  3. सूची में 1s की संख्या की गणना करें, और उस मान को लौटाएं।

3

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

चूँकि आप चाहते हैं कि आपका तरीका पहला परिणाम लौटाए जब rInt [i] बराबर rInt [गिनती], केवल उपर्युक्त चर को लागू करना पर्याप्त नहीं है क्योंकि विधि अंतिम परिणाम को तब लौटाएगी जब rInt [i] rInt [गिनती] के बराबर होता है। एक विकल्प दो "ब्रेक स्टेटमेंट" को लागू करना है, जिन्हें तब कहा जाता है जब हमारे पास वांछित परिणाम होता है। तो, विधि कुछ इस तरह दिखाई देगी:

private static int oneRun(int range) {

        int finalResult = 0; // the above-mentioned variable
        int[] rInt = new int[range + 1];
        rInt[0] = generator.nextInt(range);

        for (int count = 1; count <= range; count++) {
            rInt[count] = generator.nextInt(range);
            for (int i = 0; i < count; i++) {
                if (rInt[i] == rInt[count]) {
                    finalResult = count;
                    break; // this breaks the inside loop
                }
            }
            if (finalResult == count) {
                break; // this breaks the outside loop
            }
        }
        return finalResult;
    }

2

मैं इस बात से सहमत हूं कि एक अपवाद छोड़ देना चाहिए जहां पहुंच से बाहर बयान होता है। बस यह दिखाना चाहता था कि एक ही विधि इसे और अधिक पठनीय तरीके से कैसे कर सकती है (जावा 8 स्ट्रीम आवश्यक)।

private static int oneRun(int range) {
    int[] rInt = new int[range + 1];
    return IntStream
        .rangeClosed(0, range)
        .peek(i -> rInt[i] = generator.nextInt(range))
        .filter(i -> IntStream.range(0, i).anyMatch(j -> rInt[i] == rInt[j]))
        .findFirst()
        .orElseThrow(() -> new RuntimeException("Shouldn't be reached!"));
}

-1
private static int oneRun(int range) {
    int result = -1; // use this to store your result
    int[] rInt = new int[range+1]; // Stores the past sequence of ints.
    rInt[0] = generator.nextInt(range); // Inital random number.

    for (int count = 1; count <= range && result == -1; count++) { // Run until result found.
        rInt[count] = generator.nextInt(range); // Add randint to current iteration.   
        for (int i = 0; i < count && result == -1; i++) { // Check for past occurence and leave after result found.
            if (rInt[i] == rInt[count]) {
                result = count;
            }
        }
    }
    return result; // return your result
}

कोड भी अक्षम है क्योंकि एक बहुत सारे result == -1चेक करेगा जो returnलूप के अंदर से छोड़ा जा सकता है ...
विल्म वान ओन्सेम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.