रीडिंग लूप को समाप्त करने का कौन सा तरीका पसंदीदा तरीका है?


13

जब आपको एक पाठक को पुनरावृत्त करना होता है जहां पढ़ने के लिए मदों की संख्या अज्ञात होती है, और ऐसा करने का एकमात्र तरीका अंत तक हिट होने तक पढ़ना जारी रखना है।

यह अक्सर वह स्थान होता है जिसकी आपको अंतहीन लूप की आवश्यकता होती है।

  1. हमेशा trueऐसा होता है जो इंगित करता है कि ब्लॉक के अंदर कहींbreak या एक returnबयान होना चाहिए ।

    int offset = 0;
    while(true)
    {
        Record r = Read(offset);
        if(r == null)
        {
            break;
        }
        // do work
        offset++;
    }
  2. नहीं है डबल पाश विधि के लिए पढ़ने के लिए।

    Record r = Read(0);
    for(int offset = 0; r != null; offset++)
    {
        r = Read(offset);
        if(r != null)
        {
            // do work
        }
    }
  3. लूप करते समय एक ही रीड होता है। सभी भाषाएँ इस पद्धति का समर्थन नहीं करती हैं

    int offset = 0;
    Record r = null;
    while((r = Read(++offset)) != null)
    {
        // do work
    }

मैं सोच रहा हूं कि कौन सा दृष्टिकोण बग को लागू करने की सबसे कम संभावना है, सबसे पठनीय और आमतौर पर इस्तेमाल किया जाता है।

हर बार मुझे इनमें से एक लिखना होगा जो मुझे लगता है कि "एक बेहतर तरीका होना चाहिए"


2
आप एक ऑफसेट क्यों बनाए रख रहे हैं? अधिकांश स्ट्रीम रीडर्स आपको केवल "अगला पढ़ने" की अनुमति नहीं देते हैं
रॉबर्ट हार्वे

मेरे वर्तमान की जरूरत में @RobertHarvey पाठक के पास एक अंतर्निहित SQL क्वेरी है जो ऑफ़सेट्स को पृष्ठांकित परिणामों का उपयोग करता है। मुझे नहीं पता कि क्वेरी परिणाम कब तक हैं जब तक यह एक खाली परिणाम नहीं देता है। लेकिन, सवाल के लिए यह वास्तव में एक आवश्यकता नहीं है।
रिएक्टगुलर

3
आप भ्रमित लग रहे हैं - प्रश्न शीर्षक अंतहीन छोरों के बारे में है, लेकिन प्रश्न पाठ सभी छोरों को समाप्त करने के बारे में है। शास्त्रीय समाधान (संरचित प्रोग्रामिंग के दिनों से) एक पूर्व-रीड, लूप करना है जब आपके पास डेटा होता है, और लूप में अंतिम क्रिया के रूप में फिर से पढ़ा जाता है। यह सरल है (बग की आवश्यकता को पूरा करना), सबसे आम (क्योंकि यह 50 वर्षों के लिए लिखा गया है)। अधिकांश पठनीय राय का विषय है।
andy256

@ @y256 उलझन मेरे लिए पूर्व-कॉफी स्थिति है।
रिएक्टगुलर

1
आह, इसलिए सही प्रक्रिया 1 है) कीबोर्ड से बचते हुए कॉफी पीना, 2) कोडिंग लूप शुरू करना।
andy256

जवाबों:


49

मैं यहां एक कदम पीछे हटूंगा। आप कोड के picky विवरण पर ध्यान केंद्रित कर रहे हैं, लेकिन बड़ी तस्वीर गायब है। चलिए आपके एक उदाहरण लूप पर नज़र डालते हैं:

int offset = 0;
while(true)
{
    Record r = Read(offset);
    if(r == null)
    {
        break;
    }
    // do work
    offset++;
}

इस कोड का अर्थ क्या है ? अर्थ है "एक फ़ाइल में प्रत्येक रिकॉर्ड के लिए कुछ काम करें"। लेकिन ऐसा नहीं है कि कोड कैसा दिखता है । कोड ऐसा दिखता है "एक ऑफसेट बनाए रखें। एक फ़ाइल खोलें। बिना किसी अंतिम शर्त के एक लूप दर्ज करें। एक रिकॉर्ड पढ़ें। अशक्तता के लिए परीक्षण करें।" हम सभी को काम करने से पहले! आपके द्वारा पूछा जाने वाला प्रश्न यह है कि " मैं इस कोड की उपस्थिति को इसके शब्दार्थ से कैसे जोड़ सकता हूं? " यह कोड होना चाहिए:

foreach(Record record in RecordsFromFile())
    DoWork(record);

अब कोड अपने इरादे की तरह पढ़ता है। अपने तंत्र को अपने शब्दार्थ से अलग करें । अपने मूल कोड में आप तंत्र को मिलाते हैं - लूप का विवरण - शब्दार्थ के साथ - प्रत्येक रिकॉर्ड के लिए किया गया कार्य।

अब हमें लागू करना है RecordsFromFile()। इसे लागू करने का सबसे अच्छा तरीका क्या है? किसे पड़ी है? यह वह कोड नहीं है जिस पर किसी की नजर जा रही है। यह बुनियादी तंत्र कोड और इसकी दस पंक्तियाँ हैं। फिर भी आप इसे लिखें। इस बारे में कैसा है?

public IEnumerable<Record> RecordsFromFile()
{
    int offset = 0;
    while(true)
    {
        Record record = Read(offset);
        if (record == null) yield break;
        yield return record;
        offset += 1;
    }
}

अब जब हम रिकॉर्ड के एक आलसी गणना क्रम को जोड़ रहे हैं तो सभी प्रकार के परिदृश्य संभव हो सकते हैं:

foreach(Record record in RecordsFromFile().Take(10))
    DoWork(record);

foreach(Record record in RecordsFromFile().OrderBy(r=>r.LastName))
    DoWork(record);

foreach(Record record in RecordsFromFile().Where(r=>r.City == "London")
    DoWork(record);

और इसी तरह।

जब भी आप एक लूप लिखते हैं, तो अपने आप से पूछें "क्या यह लूप तंत्र की तरह या कोड के अर्थ की तरह पढ़ता है?" यदि उत्तर "एक तंत्र की तरह" है, तो उस तंत्र को अपने तरीके से स्थानांतरित करने का प्रयास करें, और अर्थ को अधिक दृश्यमान बनाने के लिए कोड लिखें।


3
+1 आखिरकार एक समझदार उत्तर है। यह वही है जो मैं करूँगा। धन्यवाद।
रिएक्टगुलर

1
"उस तंत्र को अपने तरीके से स्थानांतरित करने का प्रयास करें" - यह बहुत कुछ लगता है जैसे एक्स्ट्रेक्ट मेथड रीफैक्टरिंग यह नहीं करता है? "टुकड़े को एक विधि में बदल दें जिसका नाम विधि के उद्देश्य को बताता है।"
gnat

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

1
@ नागट: बिल्कुल! इस मामले में मैं जो विवरण निकालना चाहता हूं वह वह तंत्र है जिसके द्वारा पॉलिसी को बनाए रखते हुए सभी रिकॉर्ड फ़ाइल से पढ़े जाते हैं। यह नीति "हमें हर रिकॉर्ड पर कुछ काम करना है"।
एरिक लिपर्ट

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

19

आपको एक अंतहीन लूप की आवश्यकता नहीं है। आपको C # पढ़ने वाले परिदृश्यों में कभी भी एक की आवश्यकता नहीं होनी चाहिए। यह मेरा पसंदीदा तरीका है, यह मानते हुए कि आपको वास्तव में एक ऑफसेट बनाए रखने की आवश्यकता है:

Record r = Read(0);
offset=1;
while(r != null)
{
    // Do work
    r = Read(offset);
    offset++
}

यह दृष्टिकोण इस तथ्य को स्वीकार करता है कि पाठक के लिए एक सेटअप कदम है, इसलिए दो रीड विधि कॉल हैं। whileहालत, लूप के शीर्ष पर है सिर्फ मामले में रीडर में सब पर कोई डेटा नहीं है।


6

वैसे यह आपकी स्थिति पर निर्भर करता है। लेकिन अधिक "C # -ish" समाधानों में से एक जो मैं सोच सकता हूं वह अंतर्निहित IEnumerable इंटरफ़ेस और एक फॉर्च लूप का उपयोग करना है। IEnumerator के लिए इंटरफ़ेस केवल सही या गलत के साथ MoveNext को कॉल करता है, इसलिए आकार अज्ञात हो सकता है। फिर आपके समाप्ति तर्क को एक बार - एन्यूमरेटर में लिखा गया है - और आपको एक से अधिक स्थानों पर दोहराना नहीं है।

MSDN IEnumerator <T> का उदाहरण देता है । IEnumerator <T> को वापस करने के लिए आपको IEnumerable <T> बनाने की भी आवश्यकता होगी।


क्या आप एक कोड उदाहरण दे सकते हैं।
रिएक्टगुलर

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

हाँ, मुझे पता है कि आपका क्या मतलब है - बहुत अधिक उपरि। पुनरावृत्तियों की वास्तविक प्रतिभा / समस्या यह है कि वे निश्चित रूप से एक ही समय में एक संग्रह में दो चीजों को पुनरावृत्त करने में सक्षम होने के लिए निर्धारित हैं। इसलिए कई बार आपको उस कार्यक्षमता की आवश्यकता नहीं होती है ताकि आपके पास केवल IEnumerable और IEnumerator दोनों को लागू करने वाली वस्तुओं के आसपास आवरण हो सके । यह देखने का एक और तरीका है कि जो भी सामान आपके द्वारा संग्रहित किया जा रहा है उसका अंतर्निहित संग्रह मन में स्वीकृत C # प्रतिमान के साथ डिज़ाइन नहीं किया गया है। और वह ठीक है। हालांकि पुनरावृत्तियों का एक अतिरिक्त बोनस यह है कि आप सभी समानांतर LINQ सामान मुफ्त में प्राप्त कर सकते हैं!
J Trana

2

जब मेरे पास इनिशियलाइज़ेशन, कंडीशन और इन्क्रीमेंट ऑपरेशंस हैं, तो मुझे C, C ++ और C # जैसी भाषाओं के लूप्स का उपयोग करना पसंद है। ऐशे ही:

for (int offset = 0, Record r = Read(offset); r != null; r = Read(++offset)){
    // loop here!
}

या यह अगर आपको लगता है कि यह अधिक पठनीय है। मैं व्यक्तिगत रूप से पहले वाले को पसंद करता हूं।

for (int offset = 0, Record r = Read(offset); r != null; offset++, r = Read(offset)){
    // loop here!
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.