जबकि (सही) और लूप-ब्रेकिंग - एंटी-पैटर्न?


32

निम्नलिखित कोड पर विचार करें:

public void doSomething(int input)
{
   while(true)
   {
      TransformInSomeWay(input);

      if(ProcessingComplete(input))
         break;

      DoSomethingElseTo(input);
   }
}

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

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

अब, कोड इस प्रकार संरचित किया जा सकता है:

public void doSomething(int input)
{
   TransformInSomeWay(input);

   while(!ProcessingComplete(input))
   {
      DoSomethingElseTo(input);
      TransformInSomeWay(input);
   }
}

हालाँकि, यह कोड में एक विधि के लिए एक कॉल को डुप्लिकेट करता है, DRY का उल्लंघन करता है; यदि TransformInSomeWayबाद में किसी अन्य विधि से प्रतिस्थापित किया गया, तो दोनों कॉल को ढूंढना होगा और बदलना होगा (और यह तथ्य कि कोड के अधिक जटिल टुकड़े में दो कम स्पष्ट हो सकते हैं)।

आप इसे भी लिख सकते हैं:

public void doSomething(int input)
{
   var complete = false;
   while(!complete)
   {
      TransformInSomeWay(input);

      complete = ProcessingComplete(input);

      if(!complete) 
      {
         DoSomethingElseTo(input);          
      }
   }
}

... लेकिन आपके पास अब एक चर है जिसका एकमात्र उद्देश्य हालत-जाँच को लूप संरचना में स्थानांतरित करना है, और मूल तर्क के समान व्यवहार प्रदान करने के लिए कई बार जाँच भी की जानी है।

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

तो, जो "बेहतर" है? क्या लूप के चारों ओर तर्क को संरचित करते हुए लूप को कंडीशन चेकिंग की जिम्मेदारी देना बेहतर है? या तर्क को "प्राकृतिक" तरीके से संरचना करना बेहतर है जैसा कि आवश्यकताओं या एल्गोरिथ्म के एक वैचारिक विवरण से संकेत मिलता है, भले ही इसका मतलब यह हो सकता है कि लूप की अंतर्निहित क्षमताओं को दरकिनार कर दिया जाए?


12
संभवतः, लेकिन कोड के नमूने के बावजूद पूछे जाने वाले प्रश्न IMO अधिक वैचारिक है, जो एसई के इस क्षेत्र के अनुरूप है। एक पैटर्न या विरोधी पैटर्न क्या है, यहां अक्सर चर्चा की जाती है, और यही असली सवाल है; एक लूप को संरचित रूप से चलाने के लिए संरचित किया जाता है और फिर इसे एक बुरी चीज से तोड़ दिया जाता है?
कीथ्स

3
do ... untilनिर्माण के बाद से वे किए जाने की जरूरत नहीं है भी छोरों की इन प्रकार के लिए उपयोगी है "दुरुस्त।"
०५ पर ब्लरफ्ल

2
लूप-एंड-हाफ मामले में अभी भी किसी भी अच्छे जवाब का अभाव है।
लोरेन Pechtel

1
"हालांकि, यह कोड में एक विधि के लिए एक कॉल को डुप्लिकेट करता है, DRY का उल्लंघन करता है" मैं उस दावे से असहमत हूं और यह सिद्धांत की गलतफहमी जैसा लगता है।
एंडी

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

जवाबों:


14

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


"अगर-स्टेटमेंट्स, और रिपीटिंग कोड, जो सभी और भी अधिक बग प्रवण हैं।" - हाँ, जब लोगों की कोशिश कर रहा था, तो मुझे लगता है कि अगर "भविष्य के कीड़े" कहते हैं
Pat

13

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


7

मुझे ब्रेक के साथ दूसरे समाधानों को बेहतर तरीके से खोजने में परेशानी होती है। खैर, मैं एडीए तरीका पसंद करता हूं जो करने की अनुमति देता है

loop
  TransformInSomeWay(input);
exit when ProcessingComplete(input);
  DoSomethingElseTo(input);
end loop;

लेकिन ब्रेक समाधान सबसे साफ प्रतिपादन है।

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

यह पता लगाना अधिक कठिन है कि आप पाश से कैसे बाहर निकलेंगे,

गलती मत करो, चर्चा नहीं होगी यदि कठिनाई ज्यादातर समान नहीं थी: स्थिति एक ही है और एक ही स्थान पर मौजूद है।

कौन सा

while (true) {
   XXXX
if (YYY) break;
   ZZZZ;
}

तथा

while (TTTT) {
    XXXX
    if (YYYY) {
        ZZZZ
    }
}

बेहतर इरादा संरचना प्रदान? मैं पहली बार मतदान कर रहा हूं, आपको यह जांचने की जरूरत नहीं है कि स्थितियां मेल खाती हैं या नहीं। (लेकिन मेरी इंडेंटेशन देखें)।


1
ओह, देखो, एक भाषा जो सीधे मध्य-निकास छोरों का समर्थन करती है ! :)
mjfgates

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

3

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

इन भाषाओं में काम करने वाले एक अच्छे प्रोग्रामर को यह जानने में सक्षम होना चाहिए कि एक नज़र में वह क्या देख रहा है। यह एक अच्छा सा साक्षात्कार प्रश्न बना सकता है; किसी को दो छोरों को सौंपें, प्रत्येक एक मुहावरे का उपयोग करें, और उनसे पूछें कि अंतर क्या है (उचित उत्तर: "कुछ भी नहीं", शायद इसके अतिरिक्त "लेकिन मैं आदतन इसका उपयोग करता हूं।"


2

आपके विकल्प उतने बुरे नहीं हैं जितना आप सोच सकते हैं। अच्छा कोड होना चाहिए:

  1. स्पष्ट रूप से अच्छे चर नाम / टिप्पणियों के साथ इंगित करें कि लूप को किन परिस्थितियों में समाप्त किया जाना चाहिए। (ब्रेकिंग कंडीशन छिपी नहीं होनी चाहिए)

  2. स्पष्ट रूप से इंगित करें कि संचालन करने का आदेश क्या है। यह जहां DoSomethingElseTo(input)एक अच्छा विचार है अंदर वैकल्पिक 3 ऑपरेशन ( ) डाल रहा है।

उस ने कहा, परफेक्शनिस्ट, ब्रास-पॉलिशिंग वृत्ति को बहुत समय लेने देना आसान है। जैसा कि ऊपर उल्लेख किया गया है, मैं बस ट्विक करूँगा; कोड टिप्पणी करें और आगे बढ़ें।


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

खैर मैं तुम्हें कॉल नहीं कर सकते गलत :-) यह है एक राय सवाल और अच्छे कौशल पीतल-चमकाने से बाहर आया, तो: महान।
पैट

मुझे लगता है कि इसकी एक संभावना पर विचार करने के लिए, वैसे भी। मैं निश्चित रूप से कोई गारंटी नहीं देना चाहता। यह मुझे यह सोचने में बेहतर बनाता है कि मेरी पीतल-पॉलिशिंग ने मेरे कौशल में सुधार किया है, इसलिए मुझे इस बिंदु पर पूर्वाग्रह हो सकता है।
राल्फचिनपिन

2

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

आपके उदाहरण में आप उपयोग करने के लिए सही हैं while(true)क्योंकि आपका कोड स्पष्ट और समझने में आसान है। कोड को पढ़ने के लिए अक्षम और कठिन बनाने का कोई मतलब नहीं है, केवल इसलिए कि कुछ लोग इसे हमेशा उपयोग करने के लिए बुरा अभ्यास मानते हैं while(true)

मैं एक ऐसी ही समस्या का सामना कर रहा था:

$i = 0
$key = $str.'0';
$key1 = $str1.'0';
while (isset($array][$key] || isset($array][$key1])
{
    if (isset($array][$key]))
    {
        // Code
    }
    else
    {
        // Code
    }
    $key = $str.++$i;
    $key1 = $str1.$i;
}

का प्रयोग do ... while(true)से बचा जाता है isset($array][$key]अनावश्यक रूप से दो बार बुलाया जा रहा है, कोड इसे छोटा, क्लीनर, और पढ़ने में आसान। अनंत लूप बग का कोई जोखिम नहीं है else break;जो अंत में पेश किया जा रहा है ।

$i = -1;
do
{
    $key = $str.++$i;
    $key1 = $str1.$i;
    if (isset($array[$key]))
    {
        // Code
    }
    elseif (isset($array[$key1]))
    {
        // Code
    }
    else
        break;
} while (true);

अच्छी प्रथाओं का पालन करना और बुरी प्रथाओं से बचना अच्छा है, लेकिन हमेशा अपवाद होते हैं और खुले दिमाग को रखना और उन अपवादों को महसूस करना महत्वपूर्ण है।


0

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

(ध्यान दें कि इसमें लूप को आंशिक रूप से अनियंत्रित करना और लूप बॉडी को नीचे खिसकना शामिल है ताकि लूप सशर्त परीक्षण के साथ मेल खाने लगे)

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


0

आप इसके साथ भी जा सकते हैं:

public void doSomething(int input)
{
   while(TransformInSomeWay(input), !ProcessingComplete(input))
   {
      DoSomethingElseTo(input);
   }
}

या कम भ्रामक:

bool TransformAndCheckCompletion(int &input)
{
   TransformInSomeWay(input);
   return ProcessingComplete(input))
}

public void doSomething(int input)
{
   while(TransformAndCheckCompletion(input))
   {
      DoSomethingElseTo(input);
   }
}

1
जबकि कुछ लोग इसे पसंद करते हैं, मैं यह दावा करता हूं कि लूप की स्थिति आमतौर पर सशर्त परीक्षणों के लिए आरक्षित होनी चाहिए, न कि उन बयानों से जो सामान करते हैं।

5
पाश की स्थिति में कोमा अभिव्यक्ति ... यह भयानक है। तुम बहुत चालाक हो। और यह केवल इस तुच्छ मामले में काम करता है, जहां TransformInSomeWay को एक अभिव्यक्ति के रूप में व्यक्त किया जा सकता है।
gnasher729

0

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

L: for (;;) {
    if (someBool) {
        someOtherBool = doSomeWork();
        if (someOtherBool) {
            doFinalWork();
            break L;
        }
        someOtherBool2 = doSomeWork2();
        if (someOtherBool2) {
            doFinalWork2();
            break L;
        }
        someOtherBool3 = doSomeWork3();
        if (someOtherBool3) {
            doFinalWork3();
            break L;
        }
    }
    bitOfData = getTheData();
    throw new SomeException("Unable to do something for some reason.", bitOfData);
}
progressOntoOtherThings();

इस तर्क को अनबाउंड लूप ब्रेकिंग के बिना करने के लिए, मैं निम्नलिखित के साथ कुछ करना चाहूंगा:

void method() {
    if (!someBool) {
        throwingMethod();
    }
    someOtherBool = doSomeWork();
    if (someOtherBool) {
        doFinalWork();
    } else {
        someOtherBool2 = doSomeWork2();
        if (someOtherBool2) {
            doFinalWork2();
        } else {
            someOtherBool3 = doSomeWork3();
            if (someOtherBool3) {
                doFinalWork3();
            } else {
                throwingMethod();
            }
        }
    }
    progressOntoOtherThings();
}
void throwingMethod() throws SomeException {
        bitOfData = getTheData();
        throw new SomeException("Unable to do something for some reason.", bitOfData);
}

इसलिए अपवाद अब एक सहायक विधि में फेंक दिया गया है। इसके अलावा इसमें काफी if ... elseघोंसले हैं। मैं इस दृष्टिकोण से संतुष्ट नहीं हूँ। इसलिए मुझे लगता है कि पहला पैटर्न सबसे अच्छा है।


दरअसल, मैं इतने पर इस प्रश्न पूछा और आग की लपटों में मार गिराया गया ... stackoverflow.com/questions/47253486/...
intrepidis
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.