सही लूप कैसे लिखें?


65

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

/**
 * Inserts the given value in proper position in the sorted subarray i.e. 
 * array[0...rightIndex] is the sorted subarray, on inserting a new value 
 * our new sorted subarray becomes array[0...rightIndex+1].
 * @param array The whole array whose initial elements [0...rightIndex] are 
 * sorted.
 * @param rightIndex The index till which sub array is sorted.
 * @param value The value to be inserted into sorted sub array.
 */
function insert(array, rightIndex, value) {
    for(var j = rightIndex; j >= 0 && array[j] > value; j--) {
        array[j + 1] = array[j];
    }   
    array[j + 1] = value; 
};

जो गलतियाँ मैंने शुरू में की थीं, वे थीं:

  1. J> = 0 के बजाय मैंने इसे j> 0 रखा।
  2. मान गया कि क्या सरणी [j + 1] = मान या सरणी [j] = मान।

ऐसी गलतियों से बचने के लिए उपकरण / मानसिक मॉडल क्या हैं?


6
किन परिस्थितियों में आप मानते हैं कि j >= 0गलती हुई है? मैं इस तथ्य से अधिक सावधान रहूंगा कि आप पहुंच रहे हैं array[j]और array[j + 1]पहले जांच के बिना array.length > (j + 1)
बेन कॉटरेल

5
@LightnessRacesinOrbit ने जो कहा, उससे आपको समस्याएँ हल होने की संभावना है जो पहले ही हल हो चुकी हैं। आम तौर पर किसी भी लूपिंग को बोलते हुए आपको एक डेटा संरचना पर करने की आवश्यकता होती है जो पहले से ही कुछ मुख्य मॉड्यूल या वर्ग ( Array.prototypeजेएस के उदाहरण में) में मौजूद है। यह आपको mapसभी ऐरे पर काम करने जैसी कुछ स्थितियों से बढ़त की स्थिति से बचाता है । आप सभी को एक साथ लूपिंग से बचने के लिए स्लाइस और कंकट का उपयोग करके ऊपर हल कर सकते हैं: codepen.io/anon/pen/ZWovdg?editors=0012 लूप लिखने का सबसे सही तरीका है कि आप एक को न लिखें।
जेड श्नाइडर

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

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

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

जवाबों:


208

परीक्षा

नहीं, गंभीरता से, परीक्षण।

मैं 20 से अधिक वर्षों से कोडिंग कर रहा हूं और मुझे अभी भी पहली बार सही ढंग से लूप लिखने के लिए खुद पर भरोसा नहीं है। मैं परीक्षण लिखता हूं और चलाता हूं जो यह साबित करता है कि मेरे काम करने से पहले यह काम करता है। प्रत्येक सीमा स्थिति के प्रत्येक पक्ष का परीक्षण करें। उदाहरण के लिए rightIndex0 का क्या करना चाहिए? कैसे -1?

इसे सरल रखें

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

एक के बाद एक

अपनी उंगलियों को गिनने और अपनी उंगलियों के बीच रिक्त स्थान की गिनती के बीच अंतर को जानें। कभी-कभी रिक्त स्थान ऐसे होते हैं जो वास्तव में महत्वपूर्ण होते हैं। अपनी उंगलियों को विचलित न होने दें। जानिए क्या आपके अंगूठे में उंगली है। जानिए कि क्या आपके पिंकी और अंगूठे के बीच का अंतर एक स्थान के रूप में गिना जाता है।

टिप्पणियाँ

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

सबसे अच्छी टिप्पणी एक अच्छा नाम है।

यदि आप एक अच्छा नाम के साथ सब कुछ कह सकते हैं, तो आपको इसे टिप्पणी के साथ फिर से नहीं कहना चाहिए।

कपोल-कल्पना

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

संक्षिप्त नाम

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

लंबे नाम

कभी भी लाइन की लंबाई के कारण केवल एक नाम को छोटा न करें। अपना कोड बिछाने का दूसरा तरीका खोजें।

श्वेत रिक्ति

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

पाश निर्माण करता है

अपनी भाषा में लूप संरचनाओं को जानें और समीक्षा करें। डिबगर को हाइलाइट करते हुए एक for(;;)लूप देखना बहुत शिक्षाप्रद हो सकता है। सभी रूपों को जानें। while, do while, while(true), for each। सबसे सरल का उपयोग करें जिसके साथ आप दूर हो सकते हैं। पंप भड़काना देखो । यदि आपके पास है तो जानें breakऔर continueक्या करें। के बीच अंतर पता c++और ++c। जब तक आपको हमेशा वह सब कुछ बंद करने से डरना न हो जो आपको समापन की आवश्यकता है। अंत में ब्लॉक या अधिमानतः कुछ ऐसा जो इसे खोलने पर स्वचालित समापन के लिए चिह्नित करता है: संसाधनों के साथ कथन / प्रयास का उपयोग करना

लूप विकल्प

यदि आप कर सकते हैं तो कुछ और करें। यह आंखों पर आसान है और पहले से ही डीबग किया गया है। ये कई रूपों में आते हैं: संग्रह या धाराओं की अनुमति map(), reduce(), foreach(), और अन्य ऐसी तरीकों कि एक लैम्ब्डा लागू होते हैं। जैसे विशेष कार्यों के लिए देखो Arrays.fill()। पुनरावृत्ति भी होती है लेकिन केवल यह उम्मीद करते हैं कि विशेष मामलों में चीजों को आसान बनाने के लिए। आम तौर पर पुनरावृत्ति का उपयोग न करें जब तक कि आप यह नहीं देखते कि विकल्प कैसा दिखेगा।

ओह, और परीक्षण।

परीक्षण, परीक्षण, परीक्षण

क्या मैंने परीक्षण का उल्लेख किया है?

एक बात और थी। याद नहीं आ रहा। एक टी के साथ शुरू ...


36
अच्छा उत्तर, लेकिन शायद आपको परीक्षण का उल्लेख करना चाहिए। यूनिट टेस्ट में अनंत लूप से कैसे निपटता है? इस तरह के पाश 'परीक्षणों' को तोड़ नहीं है ???
GameAlchemist

139
@GameAlchemist यह पिज्जा परीक्षण है। यदि मेरा कोड उस समय चलने से नहीं रुकता है, तो मुझे पिज्जा बनाने में समय लगता है, मुझे संदेह होने लगता है कि कुछ गलत है। निश्चित रूप से यह एलन ट्यूरिंग की समस्या को दूर करने के लिए कोई इलाज नहीं है, लेकिन कम से कम मुझे इस सौदे से एक पिज़्ज़ा मिल गया है।
कैंडिड_ऑरेंज

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

15
@CodeYogi यार टीडीडी लेकिन परीक्षण >> TDD के लिए सभी कारण क्रेडिट। मूल्य का आउटपुट परीक्षण हो सकता है, आपके कोड पर आँखों का दूसरा सेट प्राप्त करना परीक्षण है (आप इसे कोड समीक्षा के रूप में औपचारिक रूप दे सकते हैं लेकिन मैं अक्सर किसी को 5 मिनट की बातचीत के लिए हड़प सकता हूं)। एक परीक्षण किसी भी मौका है जिसे आप असफल होने के अपने इरादे की अभिव्यक्ति देते हैं। नरक आप अपनी माँ के साथ एक विचार के माध्यम से बात करके अपने कोड का परीक्षण कर सकते हैं। मैंने अपने कोड में बग को टाइल में घूरते हुए पाया है। टीडीडी एक प्रभावी औपचारिक अनुशासन है जो आपको हर दुकान में नहीं मिलता है। मैंने एक बार भी कहीं भी कोडिंग नहीं की है जहाँ लोगों ने परीक्षण नहीं किया है।
कैंडिड_ऑरेंज

12
मैं कोडिंग और परीक्षण कर रहा था और सालों साल पहले मैंने टीडीडी के बारे में सुना था। यह केवल अब है कि मुझे पता है कि उन वर्षों के सहसंबंध वर्षों से बिना पैंट पहने हुए कोडिंग में बिताए गए हैं।
candied_orange

72

जब प्रोग्रामिंग यह सोचने के लिए उपयोगी है:

और जब अज्ञात क्षेत्र (जैसे सूचकांक के साथ करतब दिखाने के रूप में) की खोज यह बहुत, हो सकता है बहुत , सिर्फ उन के बारे में सोच भी नहीं करने के लिए उपयोगी है, लेकिन वास्तव में उनके साथ कोड में स्पष्ट कर दावे

आइए अपना मूल कोड लें:

/**
 * Inserts the given value in proper position in the sorted subarray i.e. 
 * array[0...rightIndex] is the sorted subarray, on inserting a new value 
 * our new sorted subarray becomes array[0...rightIndex+1].
 * @param array The whole array whose initial elements [0...rightIndex] are 
 * sorted.
 * @param rightIndex The index till which sub array is sorted.
 * @param value The value to be inserted into sorted sub array.
 */
function insert(array, rightIndex, value) {
    for(var j = rightIndex; j >= 0 && array[j] > value; j--) {
        array[j + 1] = array[j];
    }   
    array[j + 1] = value; 
};

और जो हमारे पास है उसकी जाँच करें:

  • पूर्व शर्त: array[0..rightIndex]क्रमबद्ध है
  • बाद की स्थिति: array[0..rightIndex+1]क्रमबद्ध है
  • अपरिवर्तनीय: 0 <= j <= rightIndexलेकिन यह थोड़ा बेमानी लगता है; या जैसा कि @Jules ने टिप्पणी में उल्लेख किया है, एक "दौर" के अंत में for n in [j, rightIndex+1] => array[j] > value
  • अपरिवर्तनीय: एक "दौर" के अंत में, array[0..rightIndex+1]हल किया जाता है

तो आप पहले एक is_sortedफ़ंक्शन के साथ-साथ एक minसरणी स्लाइस पर काम करने वाले फ़ंक्शन को लिख सकते हैं , और फिर दूर मुखर कर सकते हैं:

function insert(array, rightIndex, value) {
    assert(is_sorted(array[0..rightIndex]));

    for(var j = rightIndex; j >= 0 && array[j] > value; j--) {
        array[j + 1] = array[j];

        assert(min(array[j..rightIndex+1]) > value);
        assert(is_sorted(array[0..rightIndex+1]));
    }   
    array[j + 1] = value; 

    assert(is_sorted(array[0..rightIndex+1]));
};

यह भी तथ्य है कि आपकी लूप की स्थिति थोड़ी जटिल है; आप चीजों को विभाजित करके अपने आप को आसान बनाना चाहते हैं:

function insert(array, rightIndex, value) {
    assert(is_sorted(array[0..rightIndex]));

    for (var j = rightIndex; j >= 0; j--) {
        if (array[j] <= value) { break; }

        array[j + 1] = array[j];

        assert(min(array[j..rightIndex+1]) > value);
        assert(is_sorted(array[0..rightIndex+1]));
    }   
    array[j + 1] = value; 

    assert(is_sorted(array[0..rightIndex+1]));
};

अब, लूप सीधा है (से jजाता rightIndexहै 0)।

अंत में, अब इसे जांचने की आवश्यकता है:

  • सीमा की स्थिति के बारे में सोचें ( rightIndex == 0, rightIndex == array.size - 2)
  • valueसे छोटा array[0]या बड़ा होने के बारे में सोचेंarray[rightIndex]
  • के बारे में सोच valueके बराबर किया जा रहा है array[0], array[rightIndex]या कुछ बीच सूचकांक

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


8
@ कोडयुगी: टेस्ट के साथ। बात यह है कि यह व्यक्त करने के लिए अव्यावहारिक हो सकती है सब कुछ कथनों में: अगर दावे केवल कोड को दोहराता है, तो यह कुछ भी नहीं लाती नई (पुनरावृत्ति गुणवत्ता के साथ मदद नहीं करता है)। यही कारण है कि यहां मैंने लूप में जोर नहीं दिया कि 0 <= j <= rightIndexया array[j] <= value, यह सिर्फ कोड दोहराएगा। दूसरी ओर, is_sortedएक नई गारंटी देता है, इस प्रकार यह मूल्यवान है। बाद में, यही परीक्षण के लिए हैं। यदि आप insert([0, 1, 2], 2, 3)अपने फ़ंक्शन को कहते हैं और आउटपुट नहीं है, [0, 1, 2, 3]तो आपको बग मिल गया है।
मथिउ एम।

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

1
@ कंडिडऑरेन्ज: कोड का डुप्लिकेट करने से मेरा शाब्दिक अर्थ है array[j+1] = array[j]; assert(array[j+1] == array[j]);। इस स्थिति में, मान बहुत कम लगता है (यह एक कॉपी / पेस्ट है)। यदि आप अर्थ का डुप्लिकेट करते हैं लेकिन किसी अन्य फैशन में व्यक्त किया जाता है, तो यह अधिक मूल्यवान हो जाता है।
Matthieu M.

10
होरे के नियम: 1969 के बाद से सही छोरों को लिखने में मदद करना। "फिर भी, भले ही ये तकनीक एक दशक से अधिक समय से ज्ञात हो, लेकिन अधिकांश पोरग्रामर्स ने कभी भी उनके बारे में नहीं सुना है"।
जोकर_vD

1
@MatthieuM। मैं मानता हूं कि इसका बहुत कम मूल्य है। लेकिन मैं इसे कॉपी / पेस्ट होने की वजह से नहीं समझता। कहते हैं कि मैं रिफ्लेक्टर करना चाहता था insert()ताकि यह एक पुराने एरे से नए एरे में कॉपी करके काम करे। कि अपने अन्य को तोड़ने के बिना किया जा सकता है assert। लेकिन यह आखिरी नहीं है। बस दिखाता है कि आपके अन्य assertडिजाइन कितने अच्छे थे।
कैंडिड_ओरेंज

29

इकाई परीक्षण / टीडीडी का उपयोग करें

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

कल्पना कीजिए कि आपको एक ऐसी विधि को लागू करने की आवश्यकता है जो उन मूल्यों को लेती है जो शून्य से बेहतर हैं, रिवर्स ऑर्डर में। आप किन परीक्षण मामलों के बारे में सोच सकते हैं?

  1. एक अनुक्रम में एक मान होता है जो शून्य से बेहतर होता है।

    वास्तविक: [5]। अपेक्षित: [5]

    सबसे सीधा कार्यान्वयन जो आवश्यकताओं को पूरा करता है, उसमें बस कॉलर को स्रोत अनुक्रम वापस करना शामिल है।

  2. एक अनुक्रम में दो मूल्य होते हैं, दोनों शून्य से बेहतर।

    वास्तविक: [5, 7]। अपेक्षित: [7, 5]

    अब, आप केवल अनुक्रम वापस नहीं कर सकते, लेकिन आपको इसे उल्टा करना चाहिए। क्या आप एक for (;;)लूप का उपयोग करेंगे , एक अन्य भाषा निर्माण या एक पुस्तकालय विधि कोई फर्क नहीं पड़ता।

  3. एक अनुक्रम में तीन मान होते हैं, एक शून्य होता है।

    वास्तविक: [5, 0, 7]। अपेक्षित: [7, 5]

    अब आपको मानों को फ़िल्टर करने के लिए कोड बदलना चाहिए। फिर, यह एक ifबयान या अपने पसंदीदा ढांचे के लिए एक कॉल के माध्यम से व्यक्त किया जा सकता है ।

  4. आपके एल्गोरिथ्म पर निर्भर करता है (चूंकि यह सफेद-बॉक्स परीक्षण है, कार्यान्वयन मामले), आपको विशेष रूप से खाली अनुक्रम [] → []मामले को संभालने की आवश्यकता हो सकती है , या शायद नहीं। या आप यह सुनिश्चित कर सकते हैं कि किनारे का मामला जहां सभी मूल्य नकारात्मक हैं उन्हें [-4, 0, -5, 0] → []सही तरीके से संभाला जाता है, या यहां तक ​​कि सीमा नकारात्मक मूल्य हैं [6, 4, -1] → [4, 6]; [-1, 6, 4] → [4, 6]:। कई मामलों में, हालांकि, आपके पास केवल ऊपर वर्णित तीन परीक्षण होंगे: कोई भी अतिरिक्त परीक्षण आपको अपना कोड नहीं बदलने देगा, और इसलिए अप्रासंगिक होगा।

उच्च अमूर्त स्तर पर कार्य करें

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

आमतौर पर, foreachइसके बजाय इस्तेमाल किया जा सकता है for, सीमा की स्थिति को अप्रासंगिक जाँच कर रहा है: भाषा यह आपके लिए करती है। कुछ भाषाएं, जैसे कि पायथन, का for (;;)निर्माण भी नहीं है , लेकिन केवल for ... in ...

सी # में, अनुक्रमों के साथ काम करते समय LINQ विशेष रूप से सुविधाजनक है।

var result = source.Skip(5).TakeWhile(c => c > 0);

इसके forसंस्करण की तुलना में बहुत अधिक पठनीय और कम त्रुटि वाला है :

for (int i = 5; i < source.Length; i++)
{
    var value = source[i];
    if (value <= 0)
    {
        break;
    }

    yield return value;
}

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

18
: एक कमरे में हाथी का उल्लेख किया जा रहा है के लिए धन्यवाद छोरों का उपयोग नहीं कर सब परक्यों लोग अभी भी इसके 1985 की तरह कोड (और मैं उदार हूँ) मेरे से परे है। BOCTAOE।
जेरेड स्मिथ

4
@JaredSmith एक बार जब कंप्यूटर वास्तव में उस कोड को निष्पादित करता है, तो आप शर्त लगाना चाहते हैं कि वहां कोई जम्प निर्देश नहीं है? LINQ का उपयोग करके आप लूप को दूर कर रहे हैं , लेकिन यह अभी भी है। मैंने इसे सहकर्मियों को समझाया है जो शमिल के चित्रकार की कड़ी मेहनत के बारे में जानने में असफल रहे । यह समझने में असफल है कि लूप्स कहां होते हैं, भले ही वे कोड में दूर हो गए हों और परिणामस्वरूप कोड अधिक पठनीय हो, लगभग अपरिहार्य रूप से प्रदर्शन के मुद्दों की ओर जाता है कि एक को समझाने के लिए नुकसान होगा, अकेले ठीक करने दें।
एक CVn

6
@ माइकलकॉर्जलिंग: जब आप लिनक्यू का उपयोग करते हैं, तो लूप वहां होता है, लेकिन एक for(;;)निर्माण इस लूप का बहुत वर्णनात्मक नहीं होगा । एक महत्वपूर्ण पहलू यह है कि LINQ (साथ ही पायथन में सूची बोध और अन्य भाषाओं में समान तत्व) सीमा की स्थिति को ज्यादातर अप्रासंगिक बना देता है, कम से कम मूल प्रश्न के दायरे में। हालांकि, मैं यह समझने की आवश्यकता के बारे में अधिक सहमत नहीं हो सकता कि LINQ का उपयोग करते समय हुड के नीचे क्या होता है, खासकर जब यह आलसी मूल्यांकन की बात आती है।
आर्सेनी मौरज़ेंको

4
@ माइकलकॉर्जलिंग मैं जरूरी नहीं कि लिनक्यू के बारे में बात कर रहा था और मैं आपकी बात को देखने में विफल हूं। forEach, map, LazyIterator, आदि भाषा का संकलक या क्रम पर्यावरण द्वारा प्रदान की और यकीनन हैं कम वापस प्रत्येक यात्रा पर रंग बाल्टी के लिए चलने होने की संभावना। यही कारण है कि पठनीयता, और ऑफ-बाय-वन त्रुटियां दो कारण हैं, जिन्हें आधुनिक भाषाओं में जोड़ा गया है।
जेरेड स्मिथ

15

मैं अन्य लोगों से सहमत हूं जो कहते हैं कि आपके कोड का परीक्षण करें। हालाँकि, यह पहली जगह में ही सही है। मेरे पास कई मामलों में सीमा की स्थिति गलत होने की प्रवृत्ति है, इसलिए मैंने ऐसी समस्याओं को रोकने के लिए मानसिक चालें विकसित की हैं।

0-अनुक्रमित सरणी के साथ, आपकी सामान्य स्थितियां होने जा रही हैं:

for (int i = 0; i < length; i++)

या

for (int i = length - 1; i >= 0; i--)

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

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

मूल्यों में प्लग करें और अपने मस्तिष्क में कोड का मूल्यांकन करें। जितना संभव हो उतना सोचने के लिए इसे सरल बनाएं। यदि प्रासंगिक मान 0s हैं तो क्या होता है? यदि वे 1s हैं तो क्या होता है?

for(var j = rightIndex; j >= 0 && array[j] > value; j--) {
    array[j + 1] = array[j];
}   
array[j + 1] = value;

आपके उदाहरण में, आपको यकीन नहीं है कि यह होना चाहिए [j] = मान या [j + १] = मूल्य। मैन्युअल रूप से इसका मूल्यांकन शुरू करने का समय:

यदि आपकी सरणी लंबाई 0 है तो क्या होगा? उत्तर स्पष्ट हो जाता है: rightIndex होना चाहिए (लंबाई - 1) == -1, इसलिए j -1 से शुरू होता है, इसलिए इंडेक्स 0 पर सम्मिलित करने के लिए, आपको 1 जोड़ने की आवश्यकता है।

इसलिए हमने अंतिम स्थिति को सही साबित किया है, लेकिन लूप के अंदर नहीं।

यदि आपके पास 1 तत्व, 10 के साथ एक सरणी है, और हम 5 डालने की कोशिश करते हैं तो क्या होगा? एक एकल तत्व के साथ, राइटइंडेक्स 0 पर शुरू होना चाहिए। इसलिए पहली बार लूप के माध्यम से, जे = 0, इसलिए "0> = 0 और& 10> 5"। चूंकि हम 5 को इंडेक्स 0 पर सम्मिलित करना चाहते हैं, 10 को इंडेक्स 1 में ले जाना चाहिए, इसलिए एरे [1] = एरे [0]। चूँकि ऐसा तब होता है जब j 0, array [j + 1] = array [j + 0] होता है।

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

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

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


4
मुझे वास्तव में पसंद है for (int i = 0; i < length; i++)। एक बार जब मैं उस आदत में पड़ गया, तो मैंने <=लगभग उतनी बार उपयोग करना बंद कर दिया , और महसूस किया कि लूप सरल हो गए हैं। लेकिन for (int i = length - 1; i >= 0; i--)इसकी तुलना में अत्यधिक जटिल लगता है: for (int i=length; i--; )(जो कि शायद whileलूप के रूप में लिखने के लिए और भी अधिक समझदार होगा जब तक कि मैं iएक छोटा दायरा / जीवन बनाने की कोशिश नहीं कर रहा था )। परिणाम अभी भी i == लंबाई -1 (शुरू में) के साथ i == 0 के माध्यम से चलता है, केवल कार्यात्मक अंतर के साथ कि while()संस्करण i == -1 के साथ समाप्त होता है लूप के बाद (यदि यह रहता है), i = के बजाय = 0।
TOOGAM

2
@TOOGAM (int i = length; i--;) C / C ++ में काम करता है क्योंकि 0 का मूल्यांकन असत्य के रूप में किया जाता है, लेकिन सभी भाषाओं में यह समानता नहीं है। मुझे लगता है कि आप कह सकते हैं कि मैं
ब्रायस वैगनर

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

1
@BryceWagner अगर आपको करने की ज़रूरत है i-- > 0, तो क्लासिक मजाक करने की कोशिश क्यों न करें i --> 0,!
porglezomp

3
@porglezomp आह, हाँ, ऑपरेटर को जाता है । C, C ++, Java और C # सहित अधिकांश C- जैसी भाषाओं में वह है।
बजे एक CVn

11

मेरे सिर में सही कंप्यूटिंग मॉडल की कमी के कारण मैं बहुत निराश हो गया।

इस सवाल का एक बहुत ही दिलचस्प बिंदु है और इसने इस टिप्पणी को उत्पन्न किया: -

एक ही तरीका है: अपनी समस्या को बेहतर तरीके से समझें। लेकिन यह उतना ही सामान्य है जितना कि आपका प्रश्न है। - थॉमस जंक

... और थॉमस सही है। एक समारोह के लिए एक स्पष्ट इरादा नहीं होना एक लाल-झंडा होना चाहिए - एक स्पष्ट संकेत है कि आपको तुरंत रोकना चाहिए, एक पेंसिल और कागज पकड़ना चाहिए, आईडीई से कदम दूर करना चाहिए, और समस्या को ठीक से तोड़ना चाहिए; या बहुत कम से कम पवित्रता की जाँच करें कि आपने क्या किया है।

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

यदि आप समस्या को पूरी तरह से नहीं समझते हैं, तो आपको इष्टतम समाधान (या तो दक्षता या स्पष्टता के संदर्भ में) कोडिंग करने की संभावना नहीं है, और न ही आप एक टीडीडी पद्धति में वास्तव में उपयोगी इकाई परीक्षण बनाने में सक्षम होने जा रहे हैं।

अपने कोड को यहां एक उदाहरण के रूप में लें, इसमें कई संभावित खामियां हैं, जिन्हें आपने अभी तक उदाहरण के लिए नहीं माना है:

  • क्या होगा अगर राइटइंडेक्स बहुत कम है? (सुराग: इसमें डेटा हानि शामिल होगी )
  • क्या होगा अगर राइटइंडेक्स सरणी सीमा के बाहर है? (क्या आपको कोई अपवाद मिलेगा, या क्या आपने खुद को एक बफर अतिप्रवाह बनाया है?)

कोड के प्रदर्शन और डिजाइन से संबंधित कुछ अन्य मुद्दे हैं ...

  • क्या इस कोड को स्केल करने की आवश्यकता होगी? क्या ऐरे को रखना सबसे अच्छा विकल्प है, या आपको अन्य विकल्पों (जैसे लिंक्ड-लिस्ट?) को देखना चाहिए
  • क्या आप अपनी मान्यताओं के बारे में सुनिश्चित हो सकते हैं? (क्या आप गारंटी दे सकते हैं कि सरणी को छांटा गया है, और यदि यह नहीं है तो क्या होगा?)
  • क्या आप पहिया को फिर से मजबूत कर रहे हैं? सॉर्ट किए गए सरणियाँ एक प्रसिद्ध समस्या है, क्या आपने मौजूदा समाधानों का अध्ययन किया है? क्या आपकी भाषा में कोई समाधान पहले से उपलब्ध है (जैसे SortedList<t>C # में)?
  • क्या आपको एक बार में एक सरणी प्रविष्टि को मैन्युअल रूप से कॉपी करना चाहिए? या आपकी भाषा JScript की तरह सामान्य कार्य प्रदान करती है Array.Insert(...)? क्या यह कोड स्पष्ट होगा?

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


2
यहां तक ​​कि अगर आप किसी मौजूदा फ़ंक्शन (जैसे Array.Copy) के लिए अपने अनुक्रमित पास कर रहे हैं, तो भी यह बाध्य परिस्थितियों को सही करने के लिए विचार की आवश्यकता हो सकती है। कल्पना करना कि एक 0 लंबाई और 1 लंबाई और 2 लंबाई स्थितियों में क्या होता है यह सुनिश्चित करने का सबसे अच्छा तरीका हो सकता है कि आप बहुत कम या बहुत अधिक नकल नहीं कर रहे हैं।
ब्रायस वैगनर

@BryceWagner - बिल्कुल सच है, लेकिन इस समस्या के स्पष्ट विचार के बिना कि आप वास्तव में हल कर रहे हैं आप अंधेरे में 'हिट एंड होप' रणनीति के बारे में बहुत समय बिताने जा रहे हैं, जो अब तक ओपी का है इस बिंदु पर सबसे बड़ी समस्या है।
जेम्स स्नेल

2
@CodeYogi - आपके पास और जैसा कि दूसरों द्वारा बताया गया है कि आपने समस्या को उप-समस्याओं में नहीं बल्कि खराब तरीके से तोड़ा है, यही वजह है कि कई सवालों के जवाब ने इस समस्या से बचने के तरीके के रूप में आपके दृष्टिकोण का उल्लेख किया है। यह कुछ ऐसा नहीं है जिसे आपको व्यक्तिगत रूप से लेना चाहिए, बस हम में से अनुभव करें जो वहां हैं।
जेम्स स्नेल

2
@CodeYogi, मुझे लगता है कि आप इस साइट को स्टैक ओवरफ्लो के साथ भ्रमित कर सकते हैं। यह साइट कंप्यूटर टर्मिनल पर नहीं, बल्कि व्हाइटबोर्ड पर एक प्रश्नोत्तर सत्र के बराबर है । "मुझे कोड दिखाएं" एक बहुत स्पष्ट संकेत है कि आप गलत साइट पर हैं।
वाइल्डकार्ड

2
@Wildcard +1: "मुझे कोड दिखाएं", मेरे लिए, एक उत्कृष्ट संकेतक है कि क्यों यह उत्तर सही है और शायद मुझे बेहतर प्रदर्शन करने के तरीकों पर काम करने की आवश्यकता है, यह एक मानव-कारक / डिज़ाइन समस्या है जो केवल हो सकती है मानव प्रक्रिया में परिवर्तन द्वारा संबोधित किया जा सकता है - कोड की कोई भी राशि इसे नहीं सिखा सकती है।
जेम्स स्नेल

10

ऑफ-द-वन त्रुटियां सबसे आम प्रोग्रामिंग गलतियों में से एक हैं। यहां तक ​​कि अनुभवी डेवलपर्स को कभी-कभी यह गलत लगता है। उच्च स्तर की भाषाओं में आमतौर पर पुनरावृत्ति निर्माण होता है जैसे foreachया mapजो स्पष्ट अनुक्रमण को पूरी तरह से बचा लेता है। लेकिन कभी-कभी आपको स्पष्ट अनुक्रमण की आवश्यकता होती है, जैसा कि आपके उदाहरण में।

चुनौती यह है कि सरणी कोशिकाओं की श्रेणियों के बारे में कैसे सोचा जाए । स्पष्ट मानसिक मॉडल के बिना, अंत बिंदुओं को शामिल करने या बाहर करने के लिए यह भ्रमित हो जाता है।

सरणी श्रेणियों का वर्णन करते समय, कन्वेंशन को निचले बाउंड को शामिल करना है, ऊपरी बाउंड को बाहर करना है । उदाहरण के लिए सीमा 0..3 कोशिकाओं 0,1,2 है। यह सम्मेलनों 0 अनुक्रमित भाषाओं के दौरान प्रयोग किया जाता है, उदाहरण के लिए slice(start, end)जावास्क्रिप्ट में विधि subarray रिटर्न सूचकांक के साथ शुरू startकरने के लिए , लेकिन सहित नहीं सूचकांक end

यह स्पष्ट है जब आप सरणी कोशिकाओं के बीच के किनारों का वर्णन करते हुए रेंज इंडेक्स के बारे में सोचते हैं। नीचे दिया गया चित्रण लंबाई 9 की एक सरणी है, और कोशिकाओं के नीचे की संख्या किनारों से जुड़ी हुई है, और सरणी खंडों का वर्णन करने के लिए इसका उपयोग किया जाता है। उदाहरण के लिए, यह २.५ श्रेणी की कोशिकाओं की तुलना में चित्रण से स्पष्ट है।

┌───┬───┬───┬───┬───┬───┬───┬───┬───┐
│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │   -- cell indexes, e.g array[3]
└───┴───┴───┴───┴───┴───┴───┴───┴───┘
0   1   2   3   4   5   6   7   8   9   -- segment bounds, e.g. slice(2,5) 
        └───────────┘ 
          range 2..5

यह मॉडल सरणी की लंबाई के साथ संगत है जो एक सरणी की ऊपरी सीमा है। 5 लंबाई वाले एक सरणी में कोशिकाएं 0..5 होती हैं, जिसका अर्थ है कि पांच कोशिकाएं 0,1,2,3,4 हैं। इसका मतलब यह भी है कि एक खंड की लंबाई निम्न बाउंड माइनस से कम है, अर्थात सेगमेंट में 2..5 में 5-2 = 3 सेल हैं।

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

जब से आप अपने कोड में नीचे की ओर पुनरावृत्ति कर रहे हैं, आपको निम्न बाउंड, 0 को शामिल करने की आवश्यकता है, इसलिए आप इसे पुनरावृत्त करते हैं j >= 0

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

बेशक आपका कोड अतिरिक्त भ्रामक है क्योंकि यह एक के साथ सॉर्ट किए गए सबर्रे का विस्तार करता है, शुरू में सॉर्ट किए गए सबर्रे के तुरंत बाद आइटम को ओवरराइट करता है। इसलिए आप index j से पढ़ते हैं लेकिन j + 1 का मान लिखते हैं। यहां आपको केवल यह स्पष्ट होना चाहिए कि सम्मिलन से पहले जम्मू प्रारंभिक अवक्षेप में स्थिति है। जब सूचकांक संचालन बहुत मुश्किल हो जाता है तो यह मुझे ग्रिड पेपर के एक टुकड़े पर आरेखित करने में मदद करता है।


4
@ कोडयोगी: मैं कागज के एक टुकड़े पर ग्रिड के रूप में एक छोटी सी सरणी खींचता हूं, और फिर एक पेंसिल के साथ मैन्युअल रूप से लूप के एक पुनरावृत्ति के माध्यम से कदम बढ़ाता हूं। यह मुझे यह स्पष्ट करने में मदद करता है कि वास्तव में क्या होता है, उदाहरण के लिए कि कोशिकाओं की एक श्रृंखला को सही स्थानांतरित किया जाता है, और जहां नया मूल्य डाला जाता है।
जाकबीस

3
"कंप्यूटर विज्ञान में दो कठिन चीजें हैं: कैश अमान्यकरण, चीजों का नामकरण, और ऑफ-बाय-वन त्रुटियां।"
डिजिटल ट्रामा

1
@CodeYogi: मैं जो बात कर रहा हूं, उसे दिखाने के लिए एक छोटा आरेख जोड़ा गया।
जैक्सबी

1
महान अंतर्दृष्टि विशेष रूप से आप पिछले दो पार पढ़ने के लायक हैं, भ्रम की स्थिति लूप की प्रकृति के कारण भी है, यहां तक ​​कि मुझे सही सूचकांक मिल जाता है समाप्ति से एक समय पहले लूप घटता है और इसलिए मुझे एक कदम पीछे मिलता है।
कोडयोगी

1
बहुत। अति उत्कृष्ट। उत्तर। और मैं यह भी जोड़ूंगा कि यह समावेशी / अनन्य सूचकांक सम्मेलन भी myArray.Lengthया के मूल्य से प्रेरित है myList.Count- जो हमेशा शून्य-आधारित "सबसे दाहिने" सूचकांक से एक अधिक है। ... स्पष्टीकरण की लंबाई इन स्पष्ट कोडिंग उत्तराधिकार के व्यावहारिक और सरल अनुप्रयोग को मानती है। टीएल; डीआर भीड़ गायब है।
रडारबॉब

5

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

मेरा सुझाव है कि आप इस कमी को (पुनः) लूप लिखना सीखें - और मैं पेपर और पेंसिल के साथ लूप की एक सीमा के माध्यम से कुछ घंटे काम करने की सलाह दूंगा। ऐसा करने के लिए दोपहर का समय निकालें। तब तक इस विषय पर काम करते हुए 45 मिनट या एक दिन बिताएं जब तक आप वास्तव में इसे प्राप्त न करें।

यह सब बहुत अच्छी तरह से परीक्षण कर रहा है, लेकिन आपको इस उम्मीद में परीक्षण करना चाहिए कि आप आमतौर पर अपने लूप की सीमा (और अपने कोड के बाकी) सही पाते हैं।


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

3
@ मेनमा - मुझे लगता है कि मार्क अधिक संवेदनशील हो सकते थे, मुझे लगता है कि वह सही हैं - साक्षात्कार तनाव है और समस्या को परिभाषित करने के लिए बिना सोचे समझे एक साथ कोड हैक करना है। जिस तरह से प्रश्न का उत्तरार्ध में बहुत दृढ़ता से अंक दिया जाता है और यह एक ऐसी चीज है जिसे दीर्घकालिक रूप से हल किया जा सकता है सुनिश्चित करें कि आपके पास एक ठोस आधार है, आईडीई पर हैक करके नहीं
जेम्स स्नेल

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

4
@CodeYogi यदि आप 'परीक्षण और त्रुटि' कर रहे हैं और आप अपने कोडिंग के साथ 'निराश हो रहे हैं' और 'वही गलतियाँ' कर रहे हैं, तो वे संकेत हैं कि आपने अपनी समस्या को लिखना शुरू करने से पहले अच्छी तरह से नहीं समझा। । कोई भी यह नहीं कह रहा है कि आप इसे समझ नहीं पाए हैं, लेकिन यह कि आपका कोड बेहतर तरीके से सोचा जा सकता था और वे संकेत दे रहे हैं कि आप संघर्ष कर रहे हैं, जिस पर आपको चुनाव करना है और इससे सीखना या नहीं।
जेम्स स्नेल

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

3

शायद मुझे अपनी टिप्पणी पर कुछ मांस लगाना चाहिए:

एक ही तरीका है: अपनी समस्या को बेहतर तरीके से समझें। लेकिन यह उतना ही सामान्य है जितना कि आपका प्रश्न

आपकी बात

हालाँकि मुझे कुछ परीक्षण और त्रुटि के बाद मेरी धारणा सही लगी लेकिन मेरे सिर में सही कंप्यूटिंग मॉडल की कमी के कारण मैं बहुत निराश हो गया।

जब मैं पढ़ता हूं trial and error, मेरे अलार्म की घंटी बजने लगती है। बेशक हम में से बहुत से लोग मन की स्थिति को जानते हैं, जब कोई छोटी सी समस्या को ठीक करना चाहता है और उसने अपना सिर दूसरी चीजों के चारों ओर लपेट लिया है और एक या दूसरे तरीके से अनुमान लगाने लगता है कि कोड seemको करने के लिए, क्या करना चाहिए। कुछ हैकिश समाधान इससे निकलते हैं - और उनमें से कुछ शुद्ध प्रतिभाशाली हैं ; लेकिन ईमानदार होने के लिए: उनमें से ज्यादातर नहीं हैं । मुझे शामिल किया गया, इस राज्य को जानना।

अपनी ठोस समस्या से स्वतंत्र होकर, आपने प्रश्न पूछा कि सुधार कैसे करें:

1) टेस्ट

यह दूसरों द्वारा कहा गया था और मेरे पास जोड़ने के लिए कुछ भी मूल्यवान नहीं होगा

2) समस्या विश्लेषण

उसको कुछ सलाह देना कठिन है। केवल दो संकेत मैं आपको दे सकता हूं, जो संभवत: उस विषय पर आपके कौशल को बेहतर बनाने में आपकी मदद करते हैं:

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

कोड कटास एक तरीका है, जो थोड़ा मदद कर सकता है।

आप एक महान संगीतकार कैसे बन सकते हैं? यह सिद्धांत को जानने में मदद करता है, और आपके उपकरण के यांत्रिकी को समझने में मदद करता है। यह प्रतिभा रखने में मदद करता है। लेकिन अंततः, अभ्यास करने से महानता आती है; हर बार बेहतर होने के लिए फीडबैक का उपयोग करके सिद्धांत को बार-बार लागू करना।

कोड काटा

एक साइट, जो मुझे बहुत पसंद है: कोड वार्स

चुनौती के माध्यम से महारत हासिल करें वास्तविक कोड चुनौतियों पर दूसरों के साथ प्रशिक्षण करके अपने कौशल में सुधार करें

वे अपेक्षाकृत छोटी समस्याएं हैं, जो आपको अपने प्रोग्रामिंग कौशल को तेज करने में मदद करती हैं। और जो मुझे Code Wars पर सबसे ज्यादा पसंद है, वह यह है कि आप अपने समाधान की तुलना दूसरों से कर सकते हैं

या हो सकता है, आपके पास एक्सरसाइज़्म पर एक नज़र होनी चाहिए। जहां आपको समुदाय से प्रतिक्रिया मिलती है।

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

मुझे पता है - जैसा कि मैंने ऊपर कहा है कि कभी-कभी आप ऐसी अवस्था में होते हैं - कि "सरल" चीजों को और अधिक "मृत सरल" कार्यों में तोड़ना मुश्किल है; लेकिन यह बहुत मदद करता है।

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

3) एक टूलबेल विकसित करें

अपनी भाषा और अपने औजारों को जानने के अलावा - मुझे पता है कि ये ऐसी चमकदार चीजें हैं, जिनके बारे में डेवलपर्स पहले सोचते हैं - एलगोरिदम (उर्फ रीडिंग) सीखें

यहां दो किताबें शुरू करने के लिए हैं:

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

3 ए) प्रोग्रामिंग निर्माणों को जानें

यह बिंदु एक व्युत्पन्न है - इसलिए कहने के लिए। अपनी भाषा को जानें - और बेहतर: जानें, आपकी भाषा में क्या निर्माण संभव है।

बुरा या inefficent कोड के लिए एक आम बात है, कभी कभी यह है कि प्रोग्रामर छोरों के विभिन्न प्रकार के बीच अंतर पता नहीं है ( for-, while-और do-loops)। वे किसी भी तरह से सभी परस्पर प्रयोज्य हैं; लेकिन कुछ परिस्थितियों में एक और लूपिंग निर्माण चुनने से अधिक सुरुचिपूर्ण कोड हो जाता है।

और डफ का उपकरण है ...

पुनश्च:

अन्यथा आपकी टिप्पणी डोनाल्ड ट्रम्प से अच्छी नहीं है।

हाँ, हमें फिर से बढ़िया कोडिंग करनी चाहिए!

Stackoverflow के लिए एक नया आदर्श वाक्य।


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

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

"परीक्षण और त्रुटि" पर खतरे की घंटी से पूरी तरह सहमत हैं। मुझे लगता है कि समस्या को हल करने की मानसिकता को पूरी तरह से सीखने का सबसे अच्छा तरीका एल्गोरिदम और कोड को पेपर और पेंसिल से चलाना है।
वाइल्डकार्ड

उम ... आपके पास प्रोग्रामिंग के बारे में आपके जवाब के बीच में संदर्भ के बिना उद्धृत राजनीतिक उम्मीदवार पर एक गैर-अनुक्रमिक व्याकरणिक रूप से गरीब जाब क्यों है?
वाइल्डकार्ड

2

यदि मैंने समस्या को सही ढंग से समझा है, तो आपका प्रश्न यह है कि पहली कोशिश से छोरों को सही तरीके से प्राप्त करने के लिए कैसे सोचा जाए, न कि यह सुनिश्चित करने के लिए कि आपका लूप सही है (जिसके लिए उत्तर को अन्य उत्तरों में बताए अनुसार परीक्षण किया जाएगा)।

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

क्या यह एक संख्या है, जैसे 0 या 1? तब आपको सबसे अधिक संभावना एक बिंगो और बिंगो की होती है। फिर सोचें कि आप एक ही चीज को कितनी बार चलाना चाहते हैं, और आपकी अंतिम स्थिति भी होगी।

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

तकनीकी रूप से, किसी भी लूप का अनुवाद किसी अन्य लूप में किया जा सकता है, लेकिन यदि आप सही लूप का उपयोग करते हैं तो कोड को पढ़ना आसान है, इसलिए यहां कुछ सुझाव दिए गए हैं:

  1. यदि आप अपने आप को एक () {...; ब्रेक;} के लिए लिख रहे हैं, तो आपको कुछ समय चाहिए और आपके पास पहले से ही शर्त है

  2. "हालांकि मैं शायद किसी भी भाषा में सबसे अधिक इस्तेमाल किया जाने वाला लूप हूं, लेकिन इसे इमो नहीं करना चाहिए। यदि आप खुद को bool ok = true लिखते हुए पाते हैं; जबकि (जांच) {कुछ करते हैं और उम्मीद है कि कुछ बिंदु पर ठीक हो जाएगा}; तब आपको कुछ समय की आवश्यकता नहीं है, लेकिन एक समय है, क्योंकि इसका मतलब है कि आपके पास वह सब कुछ है जो आपको पहले चलना चाहिए।

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


2

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

मैं सुझाव नहीं दे रहा हूं कि आप प्रत्येक लूप के लिए ऐसा करने का प्रयास करें। लेकिन मुझे उम्मीद है कि इसमें शामिल विचार प्रक्रिया को देखने के लिए आपको उपयोगी मिलेगा।

ऐसा करने के लिए, मैं आपकी विधि का अनुवाद Microsoft Dafny नामक टूल में करूँगा , जो इस तरह के विनिर्देशों की शुद्धता को साबित करने के लिए बनाया गया है। यह प्रत्येक लूप की समाप्ति की भी जांच करता है। कृपया ध्यान दें कि डैफनी में forलूप नहीं होता है इसलिए मुझे whileइसके बजाय लूप का उपयोग करना पड़ा है ।

अंत में मैं दिखाऊंगा कि आप अपने लूप के एक, निश्चित रूप से, थोड़ा सरल संस्करण डिजाइन करने के लिए इस तरह के विनिर्देशों का उपयोग कैसे कर सकते हैं। इस सरल लूप संस्करण में लूप की स्थिति j > 0और असाइनमेंट है array[j] = value- जैसा कि आपका प्रारंभिक अंतर्ज्ञान था।

डैफनी हमारे लिए साबित करेगी कि ये दोनों लूप सही हैं और एक ही काम करते हैं।

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

भाग एक - विधि के लिए एक विनिर्देश लिखना

पहली चुनौती जिसका हम सामना कर रहे हैं वह यह निर्धारित करना है कि वास्तव में विधि क्या करने वाली है। इसके लिए मैंने पूर्व और बाद की स्थितियों को डिज़ाइन किया है जो विधि के व्यवहार को निर्दिष्ट करते हैं। विनिर्देश को और अधिक सटीक बनाने के लिए मैंने इसे इंडेक्स को वापस लाने के लिए विधि को बढ़ाया है जहां valueइसे डाला गया था।

method insert(arr:array<int>, rightIndex:int, value:int) returns (index:int)
  // the method will modify the array
  modifies arr
  // the array will not be null
  requires arr != null
  // the right index is within the bounds of the array
  // but not the last item
  requires 0 <= rightIndex < arr.Length - 1
  // value will be inserted into the array at index
  ensures arr[index] == value 
  // index is within the bounds of the array
  ensures 0 <= index <= rightIndex + 1
  // the array to the left of index is not modified
  ensures arr[..index] == old(arr[..index])
  // the array to the right of index, up to right index is
  // shifted to the right by one place
  ensures arr[index+1..rightIndex+2] == old(arr[index..rightIndex+1])
  // the array to the right of rightIndex+1 is not modified
  ensures arr[rightIndex+2..] == old(arr[rightIndex+2..])

यह विनिर्देश पूरी तरह से विधि के व्यवहार को पकड़ लेता है। इस विनिर्देश के बारे में मेरा मुख्य अवलोकन यह है कि यदि प्रक्रिया को rightIndex+1इसके बजाय मान से पारित किया गया तो इसे सरल बनाया जाएगा rightIndex। लेकिन जब से मैं यह नहीं देख पा रहा हूं कि इस पद्धति को कहा जाता है, मुझे नहीं पता कि बाकी कार्यक्रम पर उस परिवर्तन का क्या प्रभाव पड़ेगा।

भाग दो - एक लूप आक्रमणकारी का निर्धारण करना

अब हमारे पास विधि के व्यवहार के लिए एक विनिर्देश है, हमें लूप व्यवहार का एक विनिर्देश जोड़ना होगा जो डैफनी को आश्वस्त करेगा कि लूप को निष्पादित करना समाप्त हो जाएगा और वांछित अंतिम स्थिति में परिणाम होगा array

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

{
    // take a copy of the initial array, so we can refer to it later
    // ghost variables do not affect program execution, they are just
    // for specification
    ghost var initialArr := arr[..];


    var j := rightIndex;
    while(j >= 0 && arr[j] > value)
       // the loop always decreases j, so it will terminate
       decreases j
       // j remains within the loop index off-by-one
       invariant -1 <= j < arr.Length
       // the right side of the array is not modified
       invariant arr[rightIndex+2..] == initialArr[rightIndex+2..]
       // the part of the array looked at by the loop so far is
       // shifted by one place to the right
       invariant arr[j+2..rightIndex+2] == initialArr[j+1..rightIndex+1]
       // the part of the array not looked at yet is not modified
       invariant arr[..j+1] == initialArr[..j+1] 
    {
        arr[j + 1] := arr[j];
        j := j-1;
    }   
    arr[j + 1] := value;
    return j+1; // return the position of the insert
}

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

ध्यान दें कि डैफनी यहाँ शुद्धता का प्रमाण प्रस्तुत कर रही है। यह शुद्धता की अधिक मजबूत गारंटी है जो संभवतः परीक्षण द्वारा प्राप्त की जा सकती है।

भाग तीन - एक सरल लूप

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

मैंने लूप को संशोधित किया है ताकि यह लूप की स्थिति और अंतिम मूल्य के बारे में आपके मूल अंतर्ज्ञान से मेल खाता हो j। मैं तर्क दूंगा कि यह लूप आपके प्रश्न में वर्णित लूप की तुलना में सरल है। यह jबजाय उपयोग करने में अधिक बार सक्षम है j+1

  1. J पर शुरू करो rightIndex+1

  2. लूप कंडीशन को में बदलें j > 0 && arr[j-1] > value

  3. को असाइनमेंट बदलें arr[j] := value

  4. भीख के बजाय लूप के अंत में लूप काउंटर को घटाएं

यहाँ कोड है। ध्यान दें कि लूप के अपरिवर्तकों को भी अब लिखना आसान है:

method insert2(arr:array<int>, rightIndex:int, value:int) returns (index:int)
  modifies arr
  requires arr != null
  requires 0 <= rightIndex < arr.Length - 1
  ensures 0 <= index <= rightIndex + 1
  ensures arr[..index] == old(arr[..index])
  ensures arr[index] == value 
  ensures arr[index+1..rightIndex+2] == old(arr[index..rightIndex+1])
  ensures arr[rightIndex+2..] == old(arr[rightIndex+2..])
{
    ghost var initialArr := arr[..];
    var j := rightIndex+1;
    while(j > 0 && arr[j-1] > value)
       decreases j
       invariant 0 <= j <= arr.Length
       invariant arr[rightIndex+2..] == initialArr[rightIndex+2..]
       invariant arr[j+1..rightIndex+2] == initialArr[j..rightIndex+1]
       invariant arr[..j] == initialArr[..j] 
    {
        j := j-1;
        arr[j + 1] := arr[j];
    }   
    arr[j] := value;
    return j;
}

भाग चार - पिछड़े लूपिंग के बारे में सलाह

कुछ वर्षों में कई लूप लिखे जाने और सही साबित होने के बाद, मुझे पीछे की तरफ लूप करने के बारे में सामान्य सलाह मिली है।

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

दुर्भाग्य से forकई भाषाओं में लूप का निर्माण इसे कठिन बना देता है।

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

पांच भाग - बोनस

पूर्णता के लिए, यदि आप rightIndex+1विधि के बजाय पास करते हैं, तो यहां आपको प्राप्त कोड है rightIndex। यह परिवर्तन उन सभी +2ऑफसेट को समाप्त कर देता है जो अन्यथा लूप की शुद्धता के बारे में सोचने के लिए आवश्यक हैं।

method insert3(arr:array<int>, rightIndex:int, value:int) returns (index:int)
  modifies arr
  requires arr != null
  requires 1 <= rightIndex < arr.Length 
  ensures 0 <= index <= rightIndex
  ensures arr[..index] == old(arr[..index])
  ensures arr[index] == value 
  ensures arr[index+1..rightIndex+1] == old(arr[index..rightIndex])
  ensures arr[rightIndex+1..] == old(arr[rightIndex+1..])
{
    ghost var initialArr := arr[..];
    var j := rightIndex;
    while(j > 0 && arr[j-1] > value)
       decreases j
       invariant 0 <= j <= arr.Length
       invariant arr[rightIndex+1..] == initialArr[rightIndex+1..]
       invariant arr[j+1..rightIndex+1] == initialArr[j..rightIndex]
       invariant arr[..j] == initialArr[..j] 
    {
        j := j-1;
        arr[j + 1] := arr[j];
    }   
    arr[j] := value;
    return j;
}


2

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

फिर भी, अधिक जटिल मामलों में यह आसान गलत पाने के लिए है, क्योंकि बात आप लिख रहे हैं आमतौर पर है नहीं डिब्बाबंद आम बात है। अधिक आधुनिक भाषाओं और पुस्तकालयों के साथ, आप आसान बात नहीं लिखते क्योंकि इसके लिए डिब्बाबंद निर्माण या कॉल है। C ++ में आधुनिक मंत्र है "कोड लिखने के बजाय एल्गोरिदम का उपयोग करें"।

इसलिए, यह सुनिश्चित करने का तरीका सही है, विशेष रूप से इस तरह की चीज़ के लिए, सीमा स्थितियों को देखना है । जहां चीजें बदलती हैं, उन किनारों पर कुछ मामलों के लिए अपने सिर में कोड के माध्यम से ट्रेस करें। अगर index == array-max, क्या होता है? किस बारे में max-1? यदि कोड एक गलत मोड़ देता है तो यह इन सीमाओं में से एक पर होगा। कुछ छोरों को पहले या अंतिम तत्व के साथ-साथ सीमा के सही होने के बारे में चिंता करने की आवश्यकता है; उदाहरण के लिए यदि आप देखें a[I]और a[I-1]क्या होता है जब Iन्यूनतम मूल्य होता है?

इसके अलावा, उन मामलों को देखें जहां (सही) पुनरावृत्तियों की संख्या चरम है: यदि सीमाएं मिल रही हैं और आपके पास 0 पुनरावृत्तियों होंगे, तो क्या यह एक विशेष मामले के बिना काम करेगा? और क्या सिर्फ 1 पुनरावृत्ति के बारे में, जहां सबसे कम बाउंड भी एक ही समय में सबसे अधिक बाउंड है?

किनारे के मामलों (प्रत्येक किनारे के दोनों किनारों) की जांच करना है कि लूप लिखते समय आपको क्या करना चाहिए, और आपको कोड समीक्षाओं में क्या करना चाहिए।


1

मैं पहले से बताए गए विषयों के बारे में स्पष्ट रहने की कोशिश करूँगा।

ऐसी गलतियों से बचने के लिए उपकरण / मानसिक मॉडल क्या हैं?

उपकरण

मेरे लिए, बेहतर forऔर whileलूप लिखने का सबसे बड़ा टूल कोई भी forया whileलूप नहीं लिखना है ।

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

वर्तमान में मेरी पसंदीदा भाषा, रूबी, ने कार्यात्मक दृष्टिकोण ( .each, .mapआदि) को पूर्ण रूप से लिया है। यह बहुत शक्तिशाली है। मैंने अभी कुछ रूबी कोड-बेस में एक त्वरित गणना की थी जिस पर मैं काम कर रहा हूं: कोड की लगभग 10.000 पंक्तियों में, शून्य forऔर लगभग 5 हैं while

अगर मुझे एक नई भाषा चुनने के लिए मजबूर किया गया, तो प्राथमिकता सूची में कार्यात्मक / डेटा आधारित लूप की तलाश बहुत अधिक होगी।

मानसिक मॉडल

ध्यान रखें कि whileआप जो अमूर्त कम से कम एक कदम ऊपर उठा सकते हैं, वह है goto। मेरी राय में, forयह बेहतर के बजाय इसे और भी बदतर बना देता है क्योंकि यह लूप के सभी तीन हिस्सों को एक साथ कसकर काटता है।

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

limit = ...;
for (idx = 0; idx < limit; idx++) { 

लेकिन बहुत अधिक जटिल कुछ भी नहीं। हो सकता है, कभी-कभी मेरे पास एक उलटी गिनती होगी, लेकिन मैं इससे बचने की पूरी कोशिश करूंगा।

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

विशेष रूप से अगर स्थिति जटिल है, तो मैं "कंडीशन-वैरिएबल" का उपयोग करूंगा, जो कि स्पॉट करने के लिए आसान है, और whileस्टेटमेंट को स्टेटमेंट में ही स्थान न दें :

repeat = true;
while (repeat) {
   repeat = false; 
   ...
   if (complex stuff...) {
      repeat = true;
      ... other complex stuff ...
   }
}

(या ऐसा कुछ, सही उपाय में, बिल्कुल।)

यह आपको एक बहुत ही आसान मानसिक मॉडल प्रदान करता है जो कि "यह चर 0 से 10 नीरसता से चल रहा है" या "यह लूप तब तक चलता है जब तक कि चर गलत / सत्य नहीं हो जाता"। ज्यादातर दिमाग इस स्तर को ठीक से संभालने में सक्षम प्रतीत होते हैं।

उम्मीद है की वो मदद करदे।


1

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

सामान्य तौर पर, याद रखें कि एक फॉर-लूप सिंटैक्टिक शुगर है जो थोड़ी देर के लूप के आसपास निर्मित होता है:

// pseudo-code!
for (init; cond; step) { body; }

के बराबर है:

// pseudo-code!
init;
while (cond) {
  body;
  step;
}

(संभवतः लूप के लिए init चरण स्थानीय में घोषित चर रखने के लिए गुंजाइश की एक अतिरिक्त परत के साथ)।

यह कई प्रकार के छोरों के लिए ठीक है, लेकिन अंतिम चरण में आने पर अजीब है जब आप पीछे की ओर चल रहे हैं। जब पीछे की ओर काम करते हैं, तो मुझे लगता है कि एक के बाद एक मूल्य के साथ लूप इंडेक्स शुरू करना आसान है और stepभाग को लूप के शीर्ष पर ले जाना है, जैसे:

auto i = v.size();  // init
while (i > 0) {  // simpler condition because i is one after
    --i;  // step before the body
    body;  // in body, i means what you'd expect
}

या, पाश के लिए:

for (i = v.size(); i > 0; ) {
    --i;  // step
    body;
}

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

for (i = v.size() - 1; i >= 0; --i) {
    body;
}

लेकिन यह एक आपदा है अगर आपका सूचकांक चर एक अहस्ताक्षरित प्रकार है (जैसा कि यह सी या सी ++ में हो सकता है)।

इसे ध्यान में रखते हुए, अपना प्रविष्टि फ़ंक्शन लिखें।

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

    function insert(array, size, value) {
      var j = size;
    
  2. जबकि नया मान पिछले तत्व से छोटा है , स्थानांतरण जारी रखें। बेशक, पिछले तत्व केवल जाँच की जा सकती अगर वहाँ है एक पिछले तत्व है, तो हम पहले जाँच करने के लिए है कि हम बहुत शुरुआत में नहीं कर रहे हैं है:

      while (j != 0 && value < array[j - 1]) {
        --j;  // now j become current
        array[j + 1] = array[j];
      }
    
  3. यह jवहीं छोड़ता है जहां हम नया मूल्य चाहते हैं।

      array[j] = value; 
    };
    

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


0

क्या आप बस इस बारे में उलझन में हैं कि forवास्तव में एक लूप क्या करता है और यह कैसे काम करता है?

for(initialization; condition; increment*)
{
    body
}
  1. सबसे पहले, इनिशियलाइज़ेशन निष्पादित किया जाता है
  2. फिर हालत जांची जाती है
  3. यदि स्थिति सही है, तो शरीर को एक बार चलाया जाता है। अगर # 6 गोटो नहीं है
  4. वेतन वृद्धि कोड निष्पादित किया जाता है
  5. गोटो # 2
  6. पाश का अंत

एक अलग निर्माण का उपयोग करके इस कोड को फिर से लिखना आपके लिए उपयोगी हो सकता है। यहाँ थोड़ी देर के लूप का उपयोग किया जाता है:

initialization
while(condition)
{
    body
    increment
}

यहाँ कुछ अन्य सुझाव दिए गए हैं:

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

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


1
डाउनवोट क्यों?
user2023861

0

अतिरिक्त अंतर्दृष्टि का प्रयास

के लिए गैर तुच्छ एल्गोरिदम छोरों के साथ, आप निम्न विधि की कोशिश कर सकते:

  1. 4 पदों के साथ एक निश्चित सरणी बनाएं , और समस्या का अनुकरण करने के लिए कुछ मान डालें ;
  2. किसी भी लूप के बिना और हार्ड-कोडित अनुक्रमित के साथ , दिए गए समस्या को हल करने के लिए अपना एल्गोरिदम लिखें ;
  3. उसके बाद, हार्ड-कोडेड indexings स्थानापन्न कुछ चर के साथ अपने कोड में iया j, और वेतन वृद्धि / आवश्यक के रूप में इन चरों घटती (लेकिन अभी तक किसी भी पाश के बिना);
  4. अपने कोड को फिर से लिखें, और दोहराए जाने वाले ब्लॉकों को एक लूप के अंदर रखें , पूर्व और पोस्ट की शर्तों को पूरा करना;
  5. [ वैकल्पिक ] अपने लूप को उस रूप में फिर से लिखना जिस रूप में आप चाहते हैं (/ / जबकि / जबकि करते हैं);
  6. सबसे महत्वपूर्ण है अपने एल्गोरिथ्म को सही ढंग से लिखना; उसके बाद, यदि आवश्यक हो तो आप अपने कोड / लूप को रिफ्लेक्टर और ऑप्टिमाइज़ करते हैं (लेकिन यह किसी पाठक के लिए कोड को गैर-तुच्छ बना सकता है)

तुम्हारी समस्या

//TODO: Insert the given value in proper position in the sorted subarray
function insert(array, rightIndex, value) { ... };

लूप के शरीर को कई बार मैन्युअल रूप से लिखें

चलो 4 पदों के साथ एक निश्चित सरणी का उपयोग करें और लूप्स के बिना, मैन्युअल रूप से एल्गोरिथ्म लिखने की कोशिश करें:

           //0 1 2 3
var array = [2,5,9,1]; //array sorted from index 0 to 2
var leftIndex = 0;
var rightIndex = 2;
var value = array[3]; //placing the last value within the array in the proper position

//starting here as 2 == rightIndex

if (array[2] > value) {
    array[3] = array[2];
} else {
    array[3] = value;
    return; //found proper position, no need to proceed;
}

if (array[1] > value) {
    array[2] = array[1];
} else {
    array[2] = value;
    return; //found proper position, no need to proceed;
}

if (array[0] > value) {
    array[1] = array[0];
} else {
    array[1] = value;
    return; //found proper position, no need to proceed;
}

array[0] = value; //achieved beginning of the array

//stopping here because there 0 == leftIndex

हार्ड-कोडित मूल्यों को हटाते हुए फिर से लिखें

//consider going from 2 to 0, going from "rightIndex" to "leftIndex"

var i = rightIndex //starting here as 2 == rightIndex

if (array[i] > value) {
    array[i+1] = array[i];
} else {
    array[i+1] = value;
    return; //found proper position, no need to proceed;
}

i--;
if (i < leftIndex) {
    array[i+1] = value; //achieved beginning of the array
    return;
}

if (array[i] > value) {
    array[i+1] = array[i];
} else {
    array[i+1] = value;
    return; //found proper position, no need to proceed;
}

i--;
if (i < leftIndex) {
    array[i+1] = value; //achieved beginning of the array
    return;
}

if (array[i] > value) {
    array[i+1] = array[i];
} else {
    array[i+1] = value;
    return; //found proper position, no need to proceed;
}

i--;
if (i < leftIndex) {
    array[i+1] = value; //achieved beginning of the array
    return;
}

//stopping here because there 0 == leftIndex

एक लूप में अनुवाद करें

के साथ while:

var i = rightIndex; //starting in rightIndex

while (true) {
    if (array[i] > value) { //refactor: this can go in the while clause
        array[i+1] = array[i];
    } else {
        array[i+1] = value;
        break; //found proper position, no need to proceed;
    }

    i--;
    if (i < leftIndex) { //refactor: this can go (inverted) in the while clause
        array[i+1] = value; //achieved beginning of the array
        break;
    }
}

रिफ्लेक्टर / रीराइट करें / लूप को अपने इच्छित तरीके से अनुकूलित करें:

के साथ while:

var i = rightIndex; //starting in rightIndex

while ((array[i] > value) && (i >= leftIndex)) {
    array[i+1] = array[i];
    i--;
}

array[i+1] = value; //found proper position, or achieved beginning of the array

साथ for:

for (var i = rightIndex; (array[i] > value) && (i >= leftIndex); i--) {
    array[i+1] = array[i];
}

array[i+1] = value; //found proper position, or achieved beginning of the array

पुनश्च: कोड मानता इनपुट मान्य है, और उस सरणी में पुनरावृत्तियाँ नहीं हैं;


-1

यही कारण है कि मैं जब भी संभव हो, अधिक अमूर्त संचालन के पक्ष में, कच्चे सूचकांकों पर काम करने वाले छोरों को लिखने से बचता हूं।

इस मामले में, मैं ऐसा कुछ करूँगा (छद्म कोड में):

array = array[:(rightIndex - 1)] + value + array[rightIndex:]

-3

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

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


1
क्या आप कृपया कुछ उदाहरण रख सकते हैं?
कोड्योगी

ओपी के पास एक उदाहरण था।
gnasher729 12

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