2009-10-28 8 views
5

मैं IEnumerator.Reset() विधि के बारे में सोच रहा हूं। मैंने एमएसडीएन दस्तावेज में पढ़ा है कि यह केवल COM इंटरऑप के लिए है। एक सी ++ प्रोग्रामर के रूप में यह मुझे IEnumerator की तरह दिखता है जो Reset का समर्थन करता है जो मैं forward iterator पर कॉल करता हूं, जबकि IEnumerator जो Reset का समर्थन नहीं करता है वास्तव में input iterator है।क्या सी #+ सी ​​++ इटरेटर्स जैसे एन्युमरेटर्स के बीच भेद से लाभ होगा?

तो मेरा प्रश्न यह है कि क्या यह समझ सही है?

मेरे प्रश्न का दूसरा भाग यह है कि यदि इनपुट इटरेटर्स और अग्रेषित करने वाले इटरेटर्स (या "एन्युमरेटर्स" को पसंद करते हैं तो) के बीच कोई अंतर होता है तो यह सी # में किसी भी लाभ का होगा? क्या यह प्रोग्रामर के बीच कुछ भ्रम को खत्म करने में मदद नहीं करेगा, जैसा कि इस SO question about cloning iterators में पाया गया है?

संपादित करें: आगे और इनपुट इटरेटर पर स्पष्टीकरण। एक इनपुट इटरेटर केवल गारंटी देता है कि आप संग्रह के सदस्यों (या जनरेटर फ़ंक्शन या इनपुट स्ट्रीम से) केवल एक बार गणना कर सकते हैं। यह ठीक है कि कैसे IENumerator सी # में काम करता है। चाहे आप दूसरी बार गणना कर सकें या नहीं, Reset समर्थित है या नहीं। एक आगे इटरेटर, इस प्रतिबंध नहीं है। आप जितनी बार चाहें सदस्यों पर गिन सकते हैं।

कुछ सी # प्रोग्रामर कम नहीं करते हैं और IEnumerator को मल्टीपास एल्गोरिदम में विश्वसनीय रूप से उपयोग नहीं किया जा सकता है। निम्नलिखित मामले पर विचार करें:

void PrintContents(IEnumerator<int> xs) 
{ 
    while (iter.MoveNext()) 
    Console.WriteLine(iter.Current); 
    iter.Reset(); 
    while (iter.MoveNext()) 
    Console.WriteLine(iter.Current); 
} 

अगर हम इस संदर्भ में PrintContents कहते हैं, कोई समस्या नहीं: में

IEnumerable<int> GenerateInts() { 
    System.Random rnd = new System.Random(); 
    for (int i=0; i < 10; ++i) 
    yield return Rnd.Next(); 
} 

PrintContents(GenerateInts()); 

हैं IEnumerator समर्थित Reset,:

List<int> ys = new List<int>() { 1, 2, 3 } 
PrintContents(ys.GetEnumerator()); 

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

+1

मुझे लगता है कि आपका मतलब है 'आईन्यूमेरेटर। रीसेट', 'IEnumerable नहीं। रासेट, है ना? –

+0

हां, धन्यवाद! उसके लिए माफ़ करना। – cdiggins

+0

दिलचस्प सवाल। लेकिन शायद आपको सी ++ की व्याख्या करनी चाहिए - थोड़ा सा बोलें, क्योंकि बहुत सारे सी # प्रोग्रामर इसे देखने जा रहे हैं। यह स्पष्ट नहीं हो सकता है कि एक इनपुट इटरेटर और आगे इटरेटर क्या है, बिल्कुल (विशेष रूप से, उनमें से एकल/मल्टीपास क्षमताओं, जो कि इस प्रश्न के लिए वास्तव में प्रासंगिक हैं) – jalf

उत्तर

2

दिलचस्प सवाल। मेरा लेना निश्चित रूप से सी # होगा लाभ। हालांकि, यह जोड़ना आसान नहीं होगा।

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

आखिरकार, दो अवधारणाओं के दायरे अलग हैं।

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

सी # में, गणक बहुत अधिक करने के लिए नहीं हैं। आखिरकार, वे सिर्फ अनुक्रम के माध्यम से एक रैखिक तरीके से चलाने के लिए डिज़ाइन किए गए हैं।

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

दुर्भाग्यवश। सी # नजरिए से

+0

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

+0

आगे फॉरवर्डर पर आप कितने पास करेंगे? यह द्विपक्षीय नहीं है, इसलिए आप उस स्थान पर वापस नहीं जा सकते जहां आप थे, और फिर से दोबारा शुरू करें। आपको इटरेटर की प्रतिलिपि बनाना है, इसलिए आपके पास अनुक्रम में एक ही स्थान पर इंगित करने वाले दो इटरेटर हैं, और फिर उन्हें अलग-अलग बढ़ाया जा सकता है। – jalf

+0

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

3

Reset एक बड़ी गलती थी। मैं Reset पर शेनानिगन्स को कॉल करता हूं। मेरी राय में, .NET प्रकार सिस्टम में "फॉरवर्ड इटरेटर्स" और "इनपुट इटरेटर्स" के बीच जो भेद आप बना रहे हैं उसे प्रतिबिंबित करने का सही तरीका IEnumerable<T> और IEnumerator<T> के बीच भेद के साथ है।

यह भी देखें this answer, जहां माइक्रोसॉफ्ट के एरिक लिपर्ट (एक अनौपचारिक कैपेक्टिय में, कोई संदेह नहीं है, मेरा मुद्दा केवल इतना है कि वह दावा करता है कि यह एक डिजाइन गलती है) में एक समान बिंदु है टिप्पणियाँ। his awesome blog भी देखें।

+1

लिंक के लिए +1। हालांकि, मैं आपके समानता से असहमत हूं। IENumerable एक C++ कंटेनर के लिए अधिक समान है http://www.sgi.com/tech/stl/Container.html – cdiggins

-1

मुझे ऐसा नहीं लगता है। मैं IEnumerable को एक अग्रेषित इटरेटर और एक इनपुट इटरेटर कहूंगा। यह आपको पीछे की तरफ जाने या अंतर्निहित संग्रह को संशोधित करने की अनुमति नहीं देता है। foreach कीवर्ड के अतिरिक्त, इटरेटर लगभग अधिकतर समय तक सोचा नहीं जाता है।

राय: इनपुट iterators के बीच का अंतर (मिल हर एक) बनाम उत्पादन iterators (हर एक के लिए कुछ करना) भी ढांचे के लिए एक अतिरिक्त के औचित्य साबित करने के तुच्छ है।इसके अलावा, एक आउटपुट इटरेटर करने के लिए, आपको एक प्रतिनिधि को इटरेटर को पास करना होगा। इनपुट इटरेटर सी # प्रोग्रामर के लिए अधिक प्राकृतिक लगता है।

प्रोग्रामर यादृच्छिक पहुंच चाहता है तो IList<T> भी है।

+0

सी ++ में एक फॉरवर्ड इटरेटर बहु-पास एल्गोरिदम का समर्थन करता है, यह ऐसा कुछ नहीं है जिसे आप विश्वसनीय रूप से एक आईनेमरेबल/IEnumerator। उदाहरण के लिए: यदि इन्यूमेरेबल उपज समारोह से उत्पन्न होता है। – cdiggins

+0

मुझे समझ में नहीं आता क्यों IENumerable बहु-पास एल्गोरिदम का समर्थन नहीं करता है। एक कस्टम इटरेटर (उपज रिटर्न का उपयोग करके) IENumerator का एक उदाहरण बनाता है। आपके पास एक ही संग्रह में एक ही बार में एकाधिक आईएन्यूमेरेटर ऑब्जेक्ट्स हो सकती हैं। –

+2

लेकिन सिर्फ एक आईनेमेरेटर दिया गया है, आप संग्रह पर एक से अधिक पास नहीं कर सकते हैं। मल्टीपास पुनरावृत्ति करने के लिए आपको अंतर्निहित संग्रह तक पहुंच की आवश्यकता है, जो कि थोड़ी सी कमी है। (चूंकि सी ++ में, इटरेटर्स को आपकी सभी पुनरावृत्तियों की ज़रूरतों को पूरा करने के लिए डिज़ाइन किया गया है। सी # में, कुछ काफी मूल एल्गोरिदम (किसी एक से अधिक पास की आवश्यकता होती है) को अवशोषण तोड़ना पड़ता है और अंतर्निहित कंटेनर तक पहुंच की आवश्यकता होती है) – jalf

1

आ रहा है:

आप लगभग सीधे IEnumerator का उपयोग कभी नहीं। आमतौर पर आप foreach कथन करते हैं, जो IEnumerable की अपेक्षा करता है।

IEnumerable _myCollection; 
... 
foreach (var item in _myCollection) { /* Do something */ } 

आप IEnumerator या तो पास नहीं करते हैं। यदि आप एक संग्रह को पास करना चाहते हैं जिसके लिए पुनरावृत्ति की आवश्यकता है, तो आप IEnumerable पास करते हैं। चूंकि IEnumerable में एक ही फ़ंक्शन है, जो IEnumerator देता है, इसका उपयोग कई बार संग्रह (एकाधिक पास) को फिर से करने के लिए किया जा सकता है।

पर IEnumerator पर फ़ंक्शन की आवश्यकता नहीं है क्योंकि यदि आप शुरू करना चाहते हैं, तो आप केवल पुराने (कचरा एकत्रित) को फेंक दें और एक नया प्राप्त करें।

1

IEnumerator<T> पूछने के साधनों के बारे में पूछे जाने वाले तरीकों के बारे में .NET ढांचे का लाभ बहुत अधिक होगा यदि यह किस योग्यता का समर्थन कर सकता है और इससे क्या वादा किया जा सकता है। IEnumerable<T> में ऐसी सुविधाएं भी सहायक होंगी, लेकिन एक गणक के प्रश्न पूछने में सक्षम होने से कोड को अनुमति मिल जाएगी जो रैपर को शामिल किए बिना बेहतर तरीके से अंतर्निहित संग्रह का उपयोग करने के लिए ReadOnlyCollection जैसे रैपर से गणक प्राप्त कर सकता है।

एक संग्रह के लिए किसी भी गणनाकर्ता को देखते हुए जो पूरी तरह से गणना करने में सक्षम है और यह बहुत बड़ा नहीं है, कोई इसे IEnumerable<T> से उत्पन्न कर सकता है जो हमेशा वस्तुओं के समान अनुक्रम उत्पन्न करेगा (विशेष रूप से शेष वस्तुओं का सेट गणनाकर्ता) अपनी संपूर्ण सामग्री को एक सरणी में पढ़कर, गणनाकर्ता को डिस्पोजेक्ट और डिस्काउंट करके, और सरणी से एक गणक प्राप्त कर रहा है (मूल त्याग किए गए गणक के स्थान पर इसका उपयोग करके), ReadOnlyCollection<T> में सरणी को लपेटकर, और उसे वापस कर रहा है। यद्यपि ऐसा दृष्टिकोण उपर्युक्त मानदंडों को पूरा करने के किसी भी प्रकार की गणना करने योग्य संग्रह के साथ काम करेगा, लेकिन उनमें से अधिकांश के साथ यह बेहद अक्षम होगा। एक अपरिवर्तनीय IEnumerable<T> में अपनी शेष सामग्री उत्पन्न करने के लिए एक गणक से पूछने का साधन होने के कारण कई प्रकार के गणक संकेतित कार्य को अधिक कुशलतापूर्वक करने की अनुमति देंगे।

संबंधित मुद्दे