एक नेस्टेड लूप से बाहर निकलना


216

यदि मेरे पास एक लूप है, जो दूसरे के भीतर निहित है, तो मैं कुशलतापूर्वक दोनों छोरों (आंतरिक और बाहरी) से सबसे तेज तरीके से कैसे निकल सकता हूं?

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

इस बारे में जाने का एक त्वरित और अच्छा तरीका क्या है?

मैं सोच रहा था कि अपवाद सस्ते नहीं हैं / केवल वास्तव में असाधारण स्थिति में फेंक दिए जाने चाहिए आदि इसलिए मुझे नहीं लगता कि यह समाधान प्रदर्शन के दृष्टिकोण से अच्छा होगा।

मुझे ऐसा नहीं लगता है कि .NET (एनॉन विधियों) में नई सुविधाओं का लाभ उठाना सही है, जो कि बहुत मौलिक है।


मैं सिर्फ यह सुनिश्चित करना चाहता था: आप ऐसा क्यों करना चाहते हैं?
जॉन लिमजप

3
आप बूलियन का उपयोग क्यों नहीं करना चाहते हैं? ऐसा करने में क्या हर्ज है?
एंथनी

VB.net में आप एक अनियंत्रित संख्या में छोरों के आसपास एक कोशिश / अंत में बयान (कोई पकड़ नहीं) लपेट सकते हैं, फिर "बाहर निकलने की कोशिश" किसी भी बिंदु पर उन सभी से बाहर निकल जाएगी।
ब्रेन २००

जवाबों:


206

ठीक है, gotoलेकिन यह बदसूरत है, और हमेशा संभव नहीं है। आप लूप्स को एक विधि (या एनॉन-विधि) में भी रख सकते हैं और returnमुख्य कोड पर वापस जाने के लिए उपयोग कर सकते हैं।

    // goto
    for (int i = 0; i < 100; i++)
    {
        for (int j = 0; j < 100; j++)
        {
            goto Foo; // yeuck!
        }
    }
Foo:
    Console.WriteLine("Hi");

बनाम:

// anon-method
Action work = delegate
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits anon-method
        }
    }
};
work(); // execute anon-method
Console.WriteLine("Hi");

ध्यान दें कि C # 7 में हमें "स्थानीय फ़ंक्शन" मिलना चाहिए, जो (सिंटैक्स tbd आदि) का अर्थ है कि इसे कुछ काम करना चाहिए:

// local function (declared **inside** another method)
void Work()
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits local function
        }
    }
};
Work(); // execute local function
Console.WriteLine("Hi");

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

37
कभी-कभी गोटो विकल्प से कम बुराई है
शॉनब

7
@ बियोवुल्फ़ - ब्रेक केवल आंतरिक लूप से बाहर निकलेगा, न कि आंतरिक और बाहरी छोरों से।
ग्रेग बीच 21

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

11
@ el.pescado पूरी तरह से सही है: कई कोडर्स को यह मानने के लिए गुमराह किया गया है कि gotoयह हानिकारक प्रति सेग है , जबकि यह बस कुछ काम करने के लिए सही साधन है, और जैसे कई उपकरणों का दुरुपयोग किया जा सकता है। इसके खिलाफ धार्मिक टकराव gotoस्पष्ट रूप से काफी मूर्खतापूर्ण और निश्चित रूप से गैर-वैज्ञानिक है।
ओ ० '।

96

C # अक्सर उपयोग किए जाने वाले दृष्टिकोण का अनुकूलन C के बाहरी मूल्य के लूप की स्थितियों के बाहरी मूल्य के चर में सेट किया जाता है (यानी अंतर INT_MAX -1का उपयोग करते हुए लूप के लिए अक्सर अच्छा विकल्प होता है):

for (int i = 0; i < 100; i++)
{
    for (int j = 0; j < 100; j++)
    {
        if (exit_condition)
        {
            // cause the outer loop to break:
            // use i = INT_MAX - 1; otherwise i++ == INT_MIN < 100 and loop will continue 
            i = int.MaxValue - 1;
            Console.WriteLine("Hi");
            // break the inner loop
            break;
        }
    }
    // if you have code in outer loop it will execute after break from inner loop    
}

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

यह दृष्टिकोण forऔर whileछोरों के साथ काम करता है लेकिन इसके लिए काम नहीं करता है foreach। यदि foreachआपके पास छिपे हुए एन्यूमरेटर तक कोड पहुंच नहीं है, तो आप इसे बदल नहीं सकते हैं (और यहां तक ​​कि अगर आपके IEnumeratorपास कुछ "MoveToEnd" विधि नहीं हो सकती है)।

उल्लिखित टिप्पणियों के लेखकों के लिए आभार: मेटा
i = INT_MAX - 1 द्वारा सुझाव / ygoe द्वारा टिप्पणी । उचित द्वारा jmbpiano द्वारा भीतरी पाश के बाद कोड के बारे में टिप्पणी blizpasta
forforeach
IntMax


@ डीआरजी: सी # के रूप में काम नहीं करेगा "ब्रेक स्टेटमेंट निकटतम एनक्लोजिंग लूप या स्विच स्टेटमेंट को समाप्त करता है जिसमें यह दिखाई देता है।" (
एमएसएनडीएन

1
@blizpasta तो? यदि वह बाहरी लूप पर स्थिति को गलत बनाता है (जैसे उसने किया), तो यह दोनों से बाहर निकल जाएगा।
पैट्रिक

51
आपको i = INT_MAX - 1 का उपयोग करना चाहिए; अन्यथा i ++ == INT_MIN <100 और लूप जारी रहेगा
मेटा

6
foreach पर कोई विचार?
ktutnik

4
@ktutnik यह foreach के साथ काम नहीं करेगा क्योंकि आपके पास छिपे हुए एन्यूमरेटर का कोड एक्सेस नहीं होगा। इसके अलावा IEnumerator कुछ "MoveToEnd" विधि नहीं है।
याने

39

यह समाधान C # पर लागू नहीं होता है

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

outer: while(fn1())
{
   while(fn2())
   {
     if(fn3()) continue outer;
     if(fn4()) break outer;
   }
}

40
इसका दु: ख यह नहीं हो सकता है कि सी # के साथ, यह कई बार क्लीनर कोड का उत्पादन करेगा।
रिकार्ड

17
मैं वास्तव में एक सेकंड के लिए उत्साहित हो गया जब तक मुझे एहसास नहीं हुआ कि यह c # के लिए नहीं था। :(
अरवो बोवेन

1
यह अवधारणा पॉवरशेल के लिए भी है, जब कोई व्यक्ति वहां समस्या को लेकर आता है। (वे केवल लेबल नाम के सामने बृहदान्त्र डालते हैं।) अब मुझे पता है कि यह पॉवरशेल-विशिष्ट नहीं है ... यह सिंटैक्स ग # में उपलब्ध गोटो लेबल के साथ असंगत होगा। PHP कुछ और उपयोग करता है: ब्रेक 3; ब्रेक स्टेटमेंट के बाद स्तरों की संख्या डालें।
यज्ञ

1
यह जावा नहीं है c
Luca Ziegler

उन लोगों के लिए जो इस सुविधा को C # में देखना चाहते हैं, अपने
कुडो को

28

बाहरी लूप में एक उपयुक्त गार्ड का उपयोग करें। आप तोड़ने से पहले आंतरिक लूप में गार्ड सेट करें।

bool exitedInner = false;

for (int i = 0; i < N && !exitedInner; ++i) {

    .... some outer loop stuff

    for (int j = 0; j < M; ++j) {

        if (sometest) {
            exitedInner = true;
            break;
        }
    }
    if (!exitedInner) {
       ... more outer loop stuff
    }
}

या बेहतर अभी तक, एक विधि में आंतरिक लूप को अमूर्त करें और झूठे वापस आने पर बाहरी लूप से बाहर निकलें।

for (int i = 0; i < N; ++i) {

    .... some outer loop stuff

    if (!doInner(i, N, M)) {
       break;
    }

    ... more outer loop stuff
}

4
ओपी ने कहा कि "मुझे बूलियन का उपयोग नहीं करना है"।
लीपर्सस्किनपिलबॉक्स हत

बहुत पास्कल-ईश ... मैं शायद एक गोटो का उपयोग करता हूं, हालांकि मैं आमतौर पर प्लेग की तरह उनसे बचता हूं।
जोनाथन लेफ़लर

21

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

के लिए जाओ:

for ( int i = 0; i < 10; ++i ) {
   for ( int j = 0; j < 10; ++j ) {
      // code
      if ( break_condition ) goto End;
      // more code
   }
}
End: ;

स्थिति:

bool exit = false;
for ( int i = 0; i < 10 && !exit; ++i ) {
   for ( int j = 0; j < 10 && !exit; ++j ) {
      // code
      if ( break_condition ) {
         exit = true;
         break; // or continue
      }
      // more code
   }
}

अपवाद:

try {
    for ( int i = 0; i < 10 && !exit; ++i ) {
       for ( int j = 0; j < 10 && !exit; ++j ) {
          // code
          if ( break_condition ) {
             throw new Exception()
          }
          // more code
       }
    }
catch ( Exception e ) {}

2
ये सभी हैक किए गए वर्कअराउंड हैं, जहां यह एक विधि में सिर्फ कारक के लिए सुपर क्लीन होगा और शुरुआती रिटर्न का उपयोग करेगा
डस्टिन गेट्ज़

3
:) सही है, यह एक सरल उपाय है, लेकिन आपको सभी आवश्यक स्थानीय डेटा को तर्कों के रूप में विधि में पारित करना होगा ... यह उन कुछ स्थानों में से एक है जहाँ गोटो उपयुक्त समाधान हो सकता है
डेविड रोड्रिग्ज़ - ड्रिबीज़

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

मैं लेबल के बाद उस अर्धविराम को उजागर करूंगा। इस तरह से वह लेबल एक ब्लॉक के अंत में भी हो सकता है। +1
तमस हेगड़ेस

@ Windowsprogrammer ओपी ने पूछा, "इस बारे में जाने का एक त्वरित और अच्छा तरीका क्या है?" गोटो एक पसंदीदा उपाय है: साफ और संक्षिप्त, एक अलग विधि को शामिल किए बिना।
सनकैट २२

16

क्या एक निजी विधि में पाश के लिए नेस्टेड को रिफलेक्टर करना संभव है? इस तरह आप लूप से बाहर निकलने की विधि से बस 'वापसी' कर सकते हैं।


अपने मूल तरीके को छोटा बनाने के साइड बेनिफिट के साथ :-)
मार्टिन कैपोडिसी

C ++ 11 लैम्ब्डा कुछ मामलों के लिए यह आसान बनाते हैं:[&] { ... return; ... }();
BCS

14

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

मेरा मानना ​​है कि लोगों की 'भावनाओं' के बारे में gotoअंततः कोड की समझ और संभावित प्रदर्शन निहितार्थ के बारे में गलत धारणाएं हैं। प्रश्न का उत्तर देने से पहले, मैं सबसे पहले कुछ विवरणों पर जाता हूँ कि यह कैसे संकलित है।

जैसा कि हम सभी जानते हैं, C # को IL में संकलित किया जाता है, जिसे बाद में एक SSA संकलक का उपयोग करके कोडांतरक के लिए संकलित किया जाता है। मैं यह सब कैसे काम करता है में थोड़ा सा जानकारी देता हूँ, और फिर सवाल का जवाब देने की कोशिश करता हूँ।

C # से IL तक

पहले हमें C # कोड का एक टुकड़ा चाहिए। चलिए शुरू करते हैं सरल:

foreach (var item in array)
{
    // ... 
    break;
    // ...
}

हुड के नीचे क्या होता है इसका एक अच्छा विचार देने के लिए मैं यह कदम से कदम उठाऊंगा।

पहला अनुवाद: foreachसमतुल्य forलूप से (नोट: मैं यहाँ एक सरणी का उपयोग कर रहा हूँ, क्योंकि मैं आईडीसोफ़रोबल के विवरण में नहीं आना चाहता हूँ - जिस स्थिति में मुझे IEnumerable का उपयोग करना होगा):

for (int i=0; i<array.Length; ++i)
{
    var item = array[i];
    // ...
    break;
    // ...
}

दूसरा अनुवाद: forऔर breakएक आसान बराबर में अनुवाद किया जाता है:

int i=0;
while (i < array.Length)
{
    var item = array[i];
    // ...
    break;
    // ...
    ++i;
}

और तीसरा अनुवाद (यह IL कोड के समतुल्य है): हम बदलते हैं breakऔर whileएक शाखा में:

    int i=0; // for initialization

startLoop:
    if (i >= array.Length) // for condition
    {
        goto exitLoop;
    }
    var item = array[i];
    // ...
    goto exitLoop; // break
    // ...
    ++i;           // for post-expression
    goto startLoop; 

जबकि संकलक इन चीजों को एक ही चरण में करता है, यह आपको प्रक्रिया में अंतर्दृष्टि प्रदान करता है। IL कोड जो C # प्रोग्राम से विकसित होता है, वह अंतिम C # कोड का शाब्दिक अनुवाद है। आप यहाँ अपने लिए देख सकते हैं: https://dotnetfiddle.net/QaiLRz ('आईएल देखें' पर क्लिक करें)

अब, आपके द्वारा यहां देखी गई एक बात यह है कि इस प्रक्रिया के दौरान, कोड अधिक जटिल हो जाता है। इसका पालन करने का सबसे आसान तरीका इस तथ्य से है कि हमें एक ही चीज़ को एक-दूसरे के लिए अधिक कोड की आवश्यकता थी। आप यह भी तर्क है कि हो सकता है foreach, for, whileऔर breakके लिए वास्तव में एक छोटी-हाथ कर रहे हैं goto, जो आंशिक रूप से सही है।

IL से असेंबलर तक

.NET JIT कंपाइलर एक SSA कंपाइलर है। मैं यहां SSA फॉर्म के सभी विवरणों में नहीं जाऊंगा और एक अनुकूलन कंपाइलर कैसे बनाऊंगा, यह अभी बहुत ज्यादा है, लेकिन क्या होगा, इसके बारे में एक बुनियादी समझ दे सकते हैं। गहरी समझ के लिए, अनुकूलन करने वाले कंपाइलरों पर पढ़ना शुरू करना सबसे अच्छा है (मुझे यह पुस्तक एक संक्षिप्त परिचय के लिए पसंद है: http://ssabook.gforge.inria.fr/latest/book.pdf ) और LLVM (llvb.org) ।

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

हालांकि, अब हमारे पास हमारे छोरों को लागू करने के लिए शाखाएं हैं। जैसा कि आपने अनुमान लगाया होगा, यह वास्तव में जेआईटी को ठीक करने जा रहे पहले कदमों में से एक है, जैसे:

    int i=0; // for initialization

    if (i >= array.Length) // for condition
    {
        goto endOfLoop;
    }

startLoop:
    var item = array[i];
    // ...
    goto endOfLoop; // break
    // ...
    ++i;           // for post-expression

    if (i >= array.Length) // for condition
    {
        goto startLoop;
    }

endOfLoop:
    // ...

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

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

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

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

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

मदद, बहुत अधिक जटिलता ... मुझे क्या करना चाहिए?

लब्बोलुआब यह है कि आपको हमेशा अपने निपटान में आपके द्वारा बनाई गई भाषा के निर्माण का उपयोग करना चाहिए, जो आमतौर पर (संक्षेप में) आपके कंपाइलर के लिए अनुमानित पैटर्न का निर्माण करेगा। (: विशेष रूप से यदि संभव हो तो अजीब शाखाओं से बचने के लिए प्रयास करें break, continue, gotoया एक returnकुछ भी नहीं के बीच में)।

यहाँ अच्छी खबर यह है कि ये प्रेडिक्टेबल पैटर्न दोनों (इंसानों के लिए) पढ़ना आसान है और स्पॉट करने के लिए आसान (कंपाइलर के लिए)।

उन पैटर्नों में से एक को SESE कहा जाता है, जो सिंगल एंट्री सिंगल एग्जिट के लिए है।

और अब हम असली सवाल पर आते हैं।

कल्पना कीजिए कि आपके पास ऐसा कुछ है:

// a is a variable.

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...

     if (i*j > a) 
     {
        // break everything
     }
  }
}

इसे प्रेडिक्टेबल पैटर्न बनाने का सबसे आसान तरीका है कि इसे ifपूरी तरह से खत्म कर दिया जाए:

int i, j;
for (i=0; i<100 && i*j <= a; ++i) 
{
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
}

अन्य मामलों में आप विधि को 2 विधियों में विभाजित कर सकते हैं:

// Outer loop in method 1:

for (i=0; i<100 && processInner(i); ++i) 
{
}

private bool processInner(int i)
{
  int j;
  for (j=0; j<100 && i*j <= a; ++j)
  {
     // ...
  }
  return i*j<=a;
}

अस्थायी चर? अच्छा, बुरा या बदसूरत?

आप लूप के भीतर से एक बूलियन वापस करने का फैसला कर सकते हैं (लेकिन मैं व्यक्तिगत रूप से SESE फॉर्म को पसंद करता हूं, क्योंकि कंपाइलर इसे कैसे देखेगा और मुझे लगता है कि यह पढ़ने के लिए क्लीनर है)।

कुछ लोग सोचते हैं कि यह एक अस्थायी चर का उपयोग करने के लिए क्लीनर है, और इस तरह से एक समाधान का प्रस्ताव करें:

bool more = true;
for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j) 
  {
     // ...
     if (i*j > a) { more = false; break; } // yuck.
     // ...
  }
  if (!more) { break; } // yuck.
  // ...
}
// ...

मैं व्यक्तिगत रूप से इस दृष्टिकोण का विरोध कर रहा हूं। फिर से देखें कि कोड कैसे संकलित किया गया है। अब इन अच्छे, प्रेडिक्टेबल पैटर्न के साथ क्या करेंगे, इसके बारे में सोचें। तस्वीर ले आओ?

बेनाम: ठीक है, मुझे इसे बाहर जादू। क्या होगा:

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

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

दूसरे शब्दों में, सबसे अच्छा मामला यह है कि यह इसके बराबर होगा:

for (int i=0; i<100; ++i) 
{
  for (int j=0; j<100; ++j)
  {
     // ...
     if (i*j > a) { goto exitLoop; } // perhaps add a comment
     // ...
  }
  // ...
}
exitLoop:

// ...

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

tl; डॉ:

जमीनी स्तर:

  • यदि संभव हो तो लूप के लिए अपनी साधारण स्थिति का उपयोग करें। उच्च स्तर की भाषा के निर्माणों से चिपके रहें जो आपके पास जितना संभव हो सके।
  • यदि सब कुछ विफल हो जाता है और आप gotoया तो साथ छोड़ दिए जाते हैं या bool moreपूर्व को प्राथमिकता देते हैं।

OTOH: मैं केवल शायद ही कभी बाहरी छोरों को तोड़ने की इच्छा रखता हूं या गोटो-इक्विव के लिए इच्छा करता हूं (और बहुत से कोड जो यकीनन अधिक स्पष्ट रूप से लिखे जा सकते हैं), हालांकि अन्य डोमेन में यह अधिक सामान्य हो सकता है .. आज ऐसा मामला था, लेकिन यह बड़े पैमाने पर था yield return। यही है, जबकि यह दिखाना आसान है कि कुछ उपयोगी मामले हैं, अधिकांश "एप्लिकेशन-स्तरीय" कोड के लिए यह संभवतः C # के साथ हताशा की बहुत कम घटना है, "सी / सी ++ से आने वाले" को छोड़कर, ;-) मुझे कभी-कभी याद आती है; इस अवसर पर रूबी का "थ्रो" (गैर अपवाद अपवाद) भी इस डोमेन में फिट बैठता है।
user2864740

1
@ Suncat2000 चर का उपयोग करने से बचने के लिए बस अधिक कोड जैसे कि चर और सशर्त शाखाएं शुरू करना goto, आपके कोड को अधिक पठनीय नहीं बनाता है - मैं तर्क दूंगा कि क्या होता है इसके ठीक विपरीत है: वास्तव में आपके नियंत्रण प्रवाह में अधिक नोड होंगे - जो बहुत अधिक है जटिलता की परिभाषा। आत्म-अनुशासन की सहायता करने के लिए इसे टालना पठनीयता के संदर्भ में काउंटर लगता है।
atlaste

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

@ कैटालस्ट, किस भाषा की "एकमॉक्लिपिश" है?
पेसियर

11

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

def do_until_equal():
  foreach a:
    foreach b:
      if a==b: return

10

आपने त्वरित, अच्छा, बूलियन का कोई उपयोग नहीं किया, गोटो का उपयोग नहीं किया, और C # का संयोजन मांगा। आप जो चाहते हैं, उसे करने के सभी संभावित तरीकों को खारिज कर दिया है।

एक गोटो का उपयोग करने के लिए सबसे त्वरित और सबसे कम बदसूरत तरीका है।


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

@ Windowsprogramm: ओपी ने "गोटो का कोई उपयोग नहीं" करने के लिए नहीं कहा। वह "दूसरी विधि पर नहीं जाना चाहता था"। यह सवाल सभी संभावित तरीकों से दूर था , लेकिन आपको लगता है कि गोटो सबसे अच्छा है।
Suncat2000

5

कभी-कभी कोड को अपने स्वयं के फ़ंक्शन में सार करने के लिए अच्छा है और एक प्रारंभिक रिटर्न का उपयोग करने की तुलना में - प्रारंभिक रिटर्न हालांकि बुराई है:)

public void GetIndexOf(Transform transform, out int outX, out int outY)
{
    outX = -1;
    outY = -1;

    for (int x = 0; x < Columns.Length; x++)
    {
        var column = Columns[x];

        for (int y = 0; y < column.Transforms.Length; y++)
        {
            if(column.Transforms[y] == transform)
            {
                outX = x;
                outY = y;

                return;
            }
        }
    }
}

1
लेकिन तब यह ओपी द्वारा पूछे गए पारंपरिक समाधान के लिए अनुमति देता है
सूर्य प्रताप

2

मैंने बहुत सारे उदाहरण देखे हैं जो "ब्रेक" का उपयोग करते हैं लेकिन कोई भी "जारी" का उपयोग नहीं करता है।

यह अभी भी आंतरिक पाश में कुछ प्रकार के एक ध्वज की आवश्यकता होगी:

while( some_condition )
{
    // outer loop stuff
    ...

    bool get_out = false;
    for(...)
    {
        // inner loop stuff
        ...

        get_out = true;
        break;
    }

    if( get_out )
    {
        some_condition=false;
        continue;
    }

    // more out loop stuff
    ...

}

1

चूँकि मैंने पहली बार breakकुछ दशक पहले सी में देखा था, इस समस्या ने मुझे परेशान कर दिया है। मैं उम्मीद कर रहा था कि कुछ भाषा वृद्धि को तोड़ने के लिए एक विस्तार होगा जो इस प्रकार काम करेगा:

break; // our trusty friend, breaks out of current looping construct.
break 2; // breaks out of the current and it's parent looping construct.
break 3; // breaks out of 3 looping constructs.
break all; // totally decimates any looping constructs in force.

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

रुको, कौन रखरखाव प्रोग्रामिंग को और अधिक करता है? :)
जेसी सी। स्लाइसर

2
जावास्क्रिप्ट में भी ब्लॉक / ब्रेक स्टेटमेंट लेबल हैं। devguru.com/Technologies/ecmascript/quickref/break.html
डेविड ग्रांट

@ क्रिस बार्टो: अच्छा! मेरा क्रिसमस बना दिया :) @ डेविड ग्रांट: तो ऐसा लगता है कि जेएस ब्रेक == सी का गोटो?
जेसी सी। स्लीकर

D ने ब्रेक / जारी रखा है
BCS

0

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

वैसे भी, नेस्टेड छोरों को तोड़ने के लिए मैं ऐसा कुछ करता हूं:

var isDone = false;
for (var x in collectionX) {
    for (var y in collectionY) {
        for (var z in collectionZ) {
            if (conditionMet) {
                // some code
                isDone = true;
            }
            if (isDone)
                break;
        }
        if (isDone) 
            break;
    }
    if (isDone)
        break;
}

... मुझे उम्मीद है कि जो मेरे जैसे लोगों के लिए मदद करते हैं, वे "गन" फैन-विरोधी हैं


आपको बताने के लिए क्षमा करें, लेकिन आपका व्याख्याता आपकी स्थिति के लिए दोषी है। यदि वह आपको असेंबली सीखने के लिए मजबूर करने के लिए परेशान होता है, तो आप जानते हैं कि 'गोटो' सिर्फ एक छलांग है (बेशक मैं इस तथ्य की अनदेखी कर रहा हूं कि यह एसी # प्रश्न है)।
सेमीरोनिर्गो

0

मैंने ऐसा ही किया। फिर भी एक वर्कअराउंड।

foreach (var substring in substrings) {
  //To be used to break from 1st loop.
  int breaker=1;
  foreach (char c in substring) {
    if (char.IsLetter(c)) {
      Console.WriteLine(line.IndexOf(c));
      \\setting condition to break from 1st loop.
      breaker=9;
      break;
    }
  }
  if (breaker==9) {
    break;
  }
}


0

एक डबल लूप को समाप्त करने का सबसे आसान तरीका सीधे पहले लूप को समाप्त करना होगा

string TestStr = "The frog jumped over the hill";
char[] KillChar = {'w', 'l'};

for(int i = 0; i < TestStr.Length; i++)
{
    for(int E = 0; E < KillChar.Length; E++)
    {
        if(KillChar[E] == TestStr[i])
        {
            i = TestStr.Length; //Ends First Loop
            break; //Ends Second Loop
        }
    }
}

यह ब्रेक का उपयोग करके काम नहीं करता है आप आंतरिक लूप को समाप्त कर देंगे। बाहरी लूप पुन: चलना जारी रखेगा।
एलेक्स लियो

0

लूप में कस्टम परिस्थितियों का उपयोग करके लूप्स को तोड़ा जा सकता है, जिससे स्वच्छ कोड हो सकता है।

    static void Main(string[] args)
    {
        bool isBreak = false;
        for (int i = 0; ConditionLoop(isBreak, i, 500); i++)
        {
            Console.WriteLine($"External loop iteration {i}");
            for (int j = 0; ConditionLoop(isBreak, j, 500); j++)
            {
                Console.WriteLine($"Inner loop iteration {j}");

                // This code is only to produce the break.
                if (j > 3)
                {
                    isBreak = true;
                }                  
            }

            Console.WriteLine("The code after the inner loop will be executed when breaks");
        }

        Console.ReadKey();
    }

    private static bool ConditionLoop(bool isBreak, int i, int maxIterations) => i < maxIterations && !isBreak;   

इस कोड के साथ हम निम्नलिखित आउटपुट प्राप्त करते हैं:

  • बाहरी पाश पुनरावृति ०
  • आंतरिक लूप पुनरावृत्ति 0
  • भीतरी लूप पुनरावृत्ति १
  • भीतर का पाश पुनरावृति २
  • आंतरिक लूप पुनरावृत्ति 3
  • आंतरिक लूप पुनरावृत्ति 4
  • आंतरिक लूप के बाद का कोड ब्रेक होने पर निष्पादित किया जाएगा

0

एक अन्य विकल्प एक स्व-संलग्न अनाम फ़ंक्शन है । कोई गोटो, कोई लेबल, कोई नया चर, कोई नया फ़ंक्शन-नाम उपयोग नहीं किया गया और शीर्ष पर अनाम-विधि उदाहरण से छोटी एक पंक्ति ।

// self-invoked-anonymous-function
new Action(() =>
{
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            return; // exits self invoked lambda expression
        }
    }
})();
Console.WriteLine("Hi");

-3

एक कस्टम अपवाद फेंको जो आउट लूप से बाहर जाता है।

यह ब्लॉक for, foreachया whileकिसी भी प्रकार के लूप और किसी भी भाषा के लिए काम करता है जो try catch exceptionब्लॉक का उपयोग करता है

try 
{
   foreach (object o in list)
   {
      foreach (object another in otherList)
      {
         // ... some stuff here
         if (condition)
         {
            throw new CustomExcpetion();
         }
      }
   }
}
catch (CustomException)
{
   // log 
}

-4
         bool breakInnerLoop=false
        for(int i=0;i<=10;i++)
        {
          for(int J=0;i<=10;i++)
          {
              if(i<=j)
                {
                    breakInnerLoop=true;
                    break;
                }
          }
            if(breakInnerLoop)
            {
            continue
            }
        }

DViljoen के उत्तर के साथ आवश्यक अंतर क्या है?
गर्ट अर्नोल्ड

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

-4

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

            for (; j < 10; j++)
            {
                //solution
                bool breakme = false;
                for (int k = 1; k < 10; k++)
                {
                   //place the condition where you want to stop it
                    if ()
                    {
                        breakme = true;
                        break;
                    }
                }

                if(breakme)
                    break;
               }

सरल और सादा। :)


ब्रेक का सुझाव देने से पहले प्रश्न पढ़ें। इसलिए डाउनवोट्स।
cmroanirgo

1
इसे अभी पढ़ें, लेकिन जब मैंने इस उत्तर को पोस्ट किया तो बूलियन का उपयोग नहीं करने का संपादित संस्करण नहीं जोड़ा गया, यही कारण है कि मैंने इस उत्तर को पोस्ट किया है .. लेकिन डाउनवोट के लिए धन्यवाद!
स्टीव

1
यह व्यक्तिगत बात नहीं है। दुर्भाग्य से, वोट अब बंद है;) यह एसओ का एक आवश्यक हिस्सा है जो शीर्ष पर सबसे अच्छा उत्तर प्राप्त करता है (जो हमेशा नहीं होता है)
cmroanirgo

-6

क्या आपने breakकीवर्ड भी देखा था ? ऊ

यह सिर्फ छद्म कोड है, लेकिन आपको यह देखने में सक्षम होना चाहिए कि मेरा क्या मतलब है:

<?php
for(...) {
    while(...) {
        foreach(...) {
            break 3;
        }
    }
}

यदि आप breakएक समारोह की तरह के बारे में सोचते हैंbreak() , तो यह पैरामीटर है कि बाहर निकलने के लिए छोरों की संख्या होगी। जैसा कि हम यहां कोड में तीसरे लूप में हैं, हम तीनों को तोड़ सकते हैं।

मैनुअल: http://php.net/break


13
Php सवाल नहीं।
ज़र्गा

-10

मुझे लगता है कि जब तक आप "बूलियन चीज़" नहीं करना चाहते हैं तब तक वास्तव में एकमात्र समाधान फेंकना होगा। जो आपको स्पष्ट रूप से नहीं करना चाहिए ..!

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