विखंडित-लूप विराम के साथ / वापसी बनाम जबकि-लूप के साथ स्पष्ट अपरिवर्तनीय और बाद की स्थिति


17

यह जाँच का सबसे लोकप्रिय तरीका है (यह मुझे लगता है) कि क्या कोई ऐरे में है:

for (int x : array)
{
    if (x == value)
        return true;
}
return false;        

हालांकि, एक किताब में मैंने कई साल पहले, शायद, Wirth या Dijkstra द्वारा पढ़ा है, यह कहा गया था कि यह शैली बेहतर है (जब अंदर बाहर निकलने के साथ थोड़ी देर की तुलना में):

int i = 0;
while (i < array.length && array[i] != value)
    i++;
return i < array.length;

इस तरह अतिरिक्त निकास स्थिति लूप इनवेरिएंट का एक स्पष्ट हिस्सा बन जाती है, कोई छिपी हुई स्थिति नहीं होती है और लूप के अंदर से बाहर निकल जाती है, सब कुछ अधिक स्पष्ट और संरचित-प्रोग्रामिंग तरीके से अधिक होता है। मैं आम तौर पर इस बाद पैटर्न जब भी संभव पसंद किया और इस्तेमाल किया forसे केवल पुनरावृति को -loop aलिए b

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

हो सकता है कि कोई एक तरीके के पक्ष में एक अच्छा तर्क दे सकता है?

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

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


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

5
क्या संभावित तरीकों
gnat

1
मुझे पता है कि वे उदाहरण हैं, लेकिन ऐसी भाषाएं हैं जिनके पास एपीआई का उपयोग होता है जो वास्तव में उस मामले को संभालते हैं। यानीcollection.contains(foo)
बेरिन लोरिट्श

2
आप इस पुस्तक को ढूंढना चाहते हैं और अब इसे फिर से पढ़ना चाहते हैं।
थोरबजोर्न रेवन एंडरसन

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

जवाबों:


19

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

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

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


56

यह आसान है।

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

दूसरा 'बेहतर' संस्करण, मुझे कई बार पढ़ना पड़ा और यह सुनिश्चित करना पड़ा कि सभी किनारे की स्थिति सही हो।

ZERO DOUBT है जो कि बेहतर कोडिंग स्टाइल है (पहला ज्यादा बेहतर है)।

अब - लोगों के लिए CLEAR क्या है यह एक व्यक्ति से दूसरे व्यक्ति में भिन्न हो सकता है। मुझे यकीन नहीं है कि इसके लिए कोई उद्देश्य मानक हैं (हालांकि इस तरह एक मंच पर पोस्ट करने और विभिन्न प्रकार के लोगों के इनपुट प्राप्त करने में मदद मिल सकती है)।

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

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


4
नया सापेक्ष है, क्योंकि यह पहले से ही 2011 के मानक में था। इसके अलावा, दूसरा डेमो स्पष्ट रूप से C ++ नहीं है।
Deduplicator

एक वैकल्पिक समाधान यदि आप एकल निकास बिंदु का उपयोग करना चाहते हैं longerLength = true, तो एक ध्वज सेट करना होगा return longerLength
कलुब

@Deduplicator दूसरा डेमो C ++ क्यों नहीं है? मैं नहीं देखता कि मैं कुछ स्पष्ट क्यों नहीं कर रहा हूँ?
Rakete1111

2
@ Rakete1111 कच्चे सरणियों की तरह कोई गुण नहीं है length। यदि इसे वास्तव में एक सरणी के रूप में घोषित किया गया था और सूचक नहीं था, तो वे उपयोग कर सकते sizeofथे, या यदि यह एक था std::array, तो सही सदस्य कार्य है size(), कोई lengthसंपत्ति नहीं है ।
इल्युसिवब्रियन

@IllusiveBrian: sizeofबाइट्स में होगा ... C ++ 17 के बाद से सबसे सामान्य है std::size()
Deduplicator

9
int i = 0;
while (i < array.length && array[i] != value)
    i++;
return i < array.length;

[…] एक संरचित-प्रोग्रामिंग तरीके से सब कुछ अधिक स्पष्ट और अधिक है।

काफी नहीं। चर iयहाँ लूप के बाहर मौजूद होता है और इस प्रकार बाहरी दायरे का हिस्सा होता है, जबकि लूप के दायरे के भीतर -लूप xका वाक्य (उद्देश्य) forकेवल मौजूद होता है। प्रोग्रामिंग के लिए संरचना पेश करने के लिए स्कोप एक बहुत महत्वपूर्ण तरीका है।



1
@ruakh मुझे यकीन नहीं है कि आपकी टिप्पणी से क्या लेना देना है। यह कुछ हद तक निष्क्रिय-आक्रामक के रूप में आता है, जैसे कि मेरा जवाब विकी पृष्ठ पर लिखे गए का विरोध करता है। कृपया विस्तार से बताएं।
null

"संरचित प्रोग्रामिंग" एक विशिष्ट अर्थ के साथ कला का एक शब्द है, और ओपी उद्देश्यपूर्ण रूप से सही है कि संस्करण # 2 संरचित प्रोग्रामिंग के नियमों के अनुरूप है जबकि संस्करण # 1 नहीं है। आपके उत्तर से, ऐसा लगता था कि आप कला शब्द से परिचित नहीं थे, और शब्द का शाब्दिक अर्थ बता रहे थे। मुझे यकीन नहीं है कि मेरी टिप्पणी निष्क्रिय-आक्रामक के रूप में क्यों आती है; मेरा मतलब सिर्फ जानकारीपूर्ण था।
ruakh

@ruakh मैं इस बात से असहमत हूं कि संस्करण # 2 हर पहलू में नियमों के अनुरूप है और मेरे उत्तर में यह समझाया गया है।
अशक्त

आप कहते हैं "मैं असहमत हूं" जैसे कि यह एक व्यक्तिपरक चीज थी, लेकिन ऐसा नहीं है। लूप के अंदर से लौटना संरचित प्रोग्रामिंग के नियमों का एक स्पष्ट उल्लंघन है। मुझे यकीन है कि कई संरचित-प्रोग्रामिंग के प्रति उत्साही न्यूनतम-स्कोप किए गए चर के प्रशंसक हैं, लेकिन यदि आप संरचित प्रोग्रामिंग से प्रस्थान करके एक चर का दायरा कम करते हैं, तो आप संरचित प्रोग्रामिंग, अवधि से विदा हो गए हैं और चर के दायरे को कम कर रहे हैं उस।
ruakh

2

दो छोरों के अलग-अलग शब्दार्थ हैं:

  • पहला लूप बस एक साधारण हां / नहीं सवाल का जवाब देता है: "क्या सरणी में वह वस्तु है जिसे मैं खोज रहा हूं?" यह संभव सबसे संक्षिप्त तरीके से करता है।

  • दूसरा लूप प्रश्न का उत्तर देता है: "यदि सरणी में वह वस्तु है जिसकी मुझे तलाश है, तो पहले मैच का सूचकांक क्या है?" फिर, यह संभव सबसे संक्षिप्त तरीके से करता है।

चूंकि दूसरे प्रश्न का उत्तर पहले के उत्तर की तुलना में सख्ती से अधिक जानकारी प्रदान करता है, आप दूसरे प्रश्न का उत्तर देने के लिए चुन सकते हैं और फिर पहले प्रश्न के उत्तर को प्राप्त कर सकते हैं। वह जो रेखा हैreturn i < array.length;वैसे भी करती है।

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

  • लूप के पहले संस्करण का उपयोग करना ठीक है।
  • पहले वेरिएंट को सिर्फ एक boolवेरिएबल सेट करने और ब्रेक में बदलना भी ठीक है। (दूसरा बचता हैreturn स्टेटमेंट को , फंक्शन रिटर्न के बजाय वेरिएबल में उपलब्ध होता है।)
  • उपयोग करना std::findठीक है (कोड का पुन: उपयोग!)।
  • हालाँकि, एक खोज को स्पष्ट रूप से कोड करना और फिर उत्तर को कम करना boolनहीं है।

अच्छा होगा यदि
डाउनवोटर्स

2

मैं एक तीसरा विकल्प सुझाऊंगा:

return array.find(value);

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

लूप के लिए एक एरे को दूसरे में बदलने की तुलना करें:

int[] doubledArray = new int[array.length];
for (int i = 0; i < array.length; i++) {
  doubledArray[i] = array[i] * 2;
}

और जावास्क्रिप्ट-शैली mapफ़ंक्शन का उपयोग कर :

array.map((value) => value * 2);

या किसी सरणी को सम्‍मिलित करें:

int sum = 0;
for (int i = 0; i < array.length; i++) {
  sum += array[i];
}

बनाम:

array.reduce(
  (sum, nextValue) => sum + nextValue,
  0
);

आपको यह समझने में कितना समय लगता है कि यह क्या करता है?

int[] newArray = new int[array.length];
int numValuesAdded = 0;

for (int i = 0; i < array.length; i++) {
  if (array[i] >= 0) {
    newArray[numValuesAdded] = array[i];
    numValuesAdded++;
  }
}

बनाम

array.filter((value) => (value >= 0));

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

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

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


2
array.findप्रश्न पूछने की सिफारिश करना , क्योंकि हमें फिर लागू करने के सर्वोत्तम तरीके पर चर्चा करनी है array.find। जब तक आप एक अंतर्निहित findऑपरेशन के साथ हार्डवेयर का उपयोग नहीं कर रहे हैं , हमें वहां एक लूप लिखना होगा।
बमर

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

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

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

-2

यह सब ठीक होने का मतलब है कि 'बेहतर' से है। व्यावहारिक प्रोग्रामर के लिए, इसका मतलब आम तौर पर कुशल होता है - यानी इस मामले में, लूप से सीधे बाहर निकलने से एक अतिरिक्त तुलना से बचा जाता है, और बूलियन निरंतर वापस जाने से डुप्लिकेट तुलना से बचा जाता है; यह चक्र बचाता है। दिज्कस्त्र कोड बनाने से अधिक चिंतित है जो सही साबित करना आसान है । [मुझे यह प्रतीत हुआ है कि यूरोप में सीएस शिक्षा अमेरिका में सीएस शिक्षा की तुलना में कहीं अधिक गंभीरता से 'सही साबित कोड' लेती है, जहां आर्थिक ताकत कोडिंग अभ्यास पर हावी होती है]


3
PMAR, प्रदर्शन-वार दोनों लूप काफी हद तक समान हैं - इन दोनों में दो तुलनाएं हैं।
Danila Piatov

1
यदि कोई वास्तव में प्रदर्शन के बारे में परवाह करता है, तो एक तेज एल्गोरिथम का उपयोग करेगा। उदाहरण के लिए सरणी को सॉर्ट करें और बाइनरी सर्च करें, या हैशटेबल का उपयोग करें।
user949300 16

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