2011-11-08 17 views
6

मेरे पास जेनेरिक पैराम्यूटेशंस नामक एक वर्ग है जो दोनों गणनीय और गणक है। इसका काम वस्तुओं की एक क्रमबद्ध सूची लेना है और क्रमशः उनके क्रमपरिवर्तन के माध्यम से पुनरावृत्ति करना है।सी # कक्षा एक ही समय में IENumerable और एक IENumerator है। इसके साथ क्या मुद्दे हैं?

उदाहरण के लिए, इस वर्ग के एक पूर्णांक implemenation निम्नलिखित के माध्यम से पुनरावृति सकता है:

GenericPermutations<int> p = new GenericPermutations<int>({ 1, 2, 3 }); 
p.nextPermutation(); // 123 
p.nextPermutation(); // 132 
p.nextPermutation(); // 213 
// etc. 

तो भावना है कि यह एक चीजें आप से अधिक की गणना कर सकते हैं की 'सूची' में शामिल है में अपनी गणनीय। यह भी एक गणक है, क्योंकि इसके काम में अगली क्रमपरिवर्तन शामिल है।

मुद्दा: मैं वर्तमान में इस वर्ग के साथ IEnumerator और IEnumerable एकीकृत करने के लिए कोशिश कर रहा हूँ, और मुझे लगता है जैसे कि यह दोनों (बल्कि IEnumerable के रूप में एक उप वर्ग का उपयोग करने से) होना चाहिए। इस प्रकार मैंने GetEnumerator विधि में एक नई जेनेरिक पैरामिशन ऑब्जेक्ट पास करके इस से दो गणक प्राप्त करने की कोशिश के साथ इस मुद्दे से परहेज किया है।

क्या यह एक बुरा विचार है? मुझे और कुछ और विचार करना चाहिए?

+0

यदि आपका गणित उदाहरण भी उन सभी गणितकर्ताओं के लिए एक उदाहरण है, जो आपसे अनुरोध करते हैं, तो आप उनके बीच राज्य कैसे बनाएंगे? –

+0

मेरा विचार था कि जब भी एक गणक का अनुरोध किया जाता है, तो मैं अपनी ऑब्जेक्ट की एक नई प्रतिलिपि बनाउंगा, और उस प्रति को गणनाकर्ता के रूप में पास कर दूंगा। तो यदि एक नया गणक अनुरोध किया जाता है, तो वर्तमान मूल में वर्तमान गणनाकर्ता की एक प्रति उत्तीर्ण की जाती है। – jtfairbank

उत्तर

8

IEnumerable और IEnumerator के जेनेरिक संस्करण का उपयोग करके अपने भ्रम (?) को कम करें।

एक क्रमपरिवर्तन संख्या IEnumerable<IEnumerable<T>> है। तो तुम

IEnumerable<IEnumerable<T>> GetPermutations(IEnumerable<T> sequence) 
{ 
    return new Permuter<T>(sequence); 
} 

और

public class Permuter<T> : IEnumerable<IEnumerable<T>> { ... } 

इसके अलावा, मैं एक से अधिक मामले में जहां एक ही प्रकार दोनों IEnumerable<T> और IEnumerator<T> कार्यान्वित देखा है की तरह कुछ हो सकता है; इसकी GetEnumerator विधि बस return this; थी।

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

संपादित करें: Permuter

var permuter = GetPermutations(sequence); 
foreach (var permutation in permuter) 
{ 
    foreach (var item in permutation) 
     Console.Write(item + "; "); 
    Console.WriteLine(); 
} 

उपभोक्ता इनपुट अनुक्रम यह मानते हुए है {1, 2, 3}, उत्पादन होता है

1; 2; 3; 
1; 3; 2; 
2; 1; 3; 
2; 3; 1; 
3; 1; 2; 
3; 2; 1; 

संपादित करें:

यहाँ एक सुपर अक्षम है सुझाव को चित्रित करने के लिए कार्यान्वयन:

public class Permuter<T> : IEnumerable<IEnumerable<T>> 
{ 
    private readonly IEnumerable<T> _sequence; 

    public Permuter(IEnumerable<T> sequence) 
    { 
     _sequence = sequence; 
    } 

    public IEnumerator<IEnumerable<T>> GetEnumerator() 
    { 
     foreach(var item in _sequence) 
     { 
      var remaining = _sequence.Except(Enumerable.Repeat(item, 1)); 
      foreach (var permutation in new Permuter<T>(remaining)) 
       yield return Enumerable.Repeat(item, 1).Concat(permutation); 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 
+0

मुझे GetEnumerator विधि में बस 'यह' गुजरना पसंद नहीं है क्योंकि प्रत्येक लूप और अन्य स्थितियों के लिए नेस्टेड में समस्याएं हो सकती हैं। आपका पहला सुझाव दिलचस्प दिखता है, क्या आप इसे और अधिक विस्तारित कर सकते हैं? मैं 'IENumerable >' द्वारा भ्रमित हूं। – jtfairbank

+1

आप किस बारे में उलझन में हैं? अनुक्रम {1, 2, 3} के क्रमिक क्रम में छह अनुक्रम ({1, 2, 3}, {1, 3, 2}, आदि शामिल हैं; इसे अनुक्रमों के अनुक्रम के रूप में माना जा सकता है। मैं एक कोड उदाहरण जोड़ दूंगा। – phoog

+0

आह मैं देखता हूं कि आपका क्या मतलब है। हालांकि मुझे खुद को अनुक्रमों पर गिनती नहीं करनी चाहिए, केवल उन्हें उत्पन्न करने वाले क्रमिक क्रमशः। – jtfairbank

0

एक ऑब्जेक्ट के लिए IEnumerator<T> और IEnumerable<T> दोनों के रूप में व्यवहार करना संभव है, लेकिन आमतौर पर ऐसी वस्तु में ऐसा करने के लिए मुश्किल होती है जैसे क्विर्की सेमेन्टिक्स से बचने के लिए; जब तक IEnumerator<T> स्टेटलेस होने वाला नहीं है (उदाहरण के लिए एक खाली गणनाकर्ता, जहां MoveNext() हमेशा झूठा, या अंतहीन-दोहराना गणनाकर्ता देता है, जहां MoveNext() कुछ भी नहीं करता है लेकिन हमेशा सत्य देता है, और Current हमेशा एक ही मान देता है), प्रत्येक कॉल GetEnumerator() पर आवश्यक है एक विशिष्ट ऑब्जेक्ट उदाहरण लौटाएं, और उस उदाहरण को IEnumerable<T> लागू करने में बहुत कम मूल्य होने की संभावना है।

होगा एक मान प्रकार लागू IEnumerable<T> और IEnumerator<T> बीत रहा है, और उसके GetEnumerator() विधि वापसी this होने, आवश्यकता है कि GetEnumerator वापसी एक अलग वस्तु दृष्टान्त की प्रत्येक कॉल, लेकिन होने मूल्य प्रकार परिवर्तनशील इंटरफेस को लागू संतुष्ट आम तौर पर खतरनाक है। यदि किसी मान प्रकार को IEnuerator<T> पर बॉक्स किया गया है और कभी भी अनबॉक्स नहीं किया गया है, तो यह क्लास-प्रकार ऑब्जेक्ट के रूप में व्यवहार करेगा, लेकिन इसका कोई वास्तविक कारण नहीं है कि यह केवल क्लास-टाइप ऑब्जेक्ट क्यों नहीं होना चाहिए था।

सी # में इटरेटर कक्षा वस्तुओं के रूप में कार्यान्वित किए जाते हैं जो IEnumerable<T> और IEnumerator<T> दोनों को लागू करते हैं, लेकिन उनमें अर्थपूर्ण शुद्धता सुनिश्चित करने के लिए फैंसी तर्क का एक उचित हिस्सा शामिल है। शुद्ध प्रभाव यह है कि एक ऑब्जेक्ट दोनों इंटरफेस को कार्यान्वित करने में प्रदर्शन में मामूली सुधार प्रदान करता है, जेनरेट कोड में जटिलता के उचित बिट के बदले में, और उनके IDisposable व्यवहार में कुछ अर्थपूर्ण quirkiness। मैं इस दृष्टिकोण को किसी भी कोड में अनुशंसा नहीं करता जिसे मानव-पठनीय होने की आवश्यकता है; चूंकि IEnumerator<T> और IEnumerable<T> कक्षा के पहलू अधिकतर विभिन्न क्षेत्रों का उपयोग करते हैं, और चूंकि एक संयुक्त वर्ग में "थ्रेड-आईडी" फ़ील्ड होना आवश्यक है, जो अलग-अलग वर्गों का उपयोग करते समय आवश्यक नहीं होगा, प्रदर्शन सुधार एक ही इसका उपयोग करके प्राप्त कर सकता है दोनों इंटरफेस के लिए लागू करने के लिए वस्तु सीमित है। यदि संभवतः कंपाइलर में जटिलता जोड़ना संभव है तो लाखों इटेटरेटर दिनचर्या में मामूली प्रदर्शन सुधार प्रदान करेगा, लेकिन एक दिनचर्या के प्रदर्शन में सुधार करने के लायक नहीं है।

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