2010-09-27 12 views
22

मैं यह देखना चाहता हूं कि IEnumerable में बिल्कुल एक तत्व है।मैं कैसे सुनिश्चित करूं कि अनुक्रम की निश्चित लंबाई हो?

bool hasOneElement = seq.Count() == 1 

हालांकि यह, बहुत ही कुशल नहीं है के रूप में Count() पूरी सूची की गणना होगी: यह टुकड़ा काम करता है। जाहिर है, एक सूची जानना खाली है या 1 से अधिक तत्व शामिल हैं इसका मतलब है कि यह खाली नहीं है। क्या कोई विस्तार विधि है जिसमें यह शॉर्ट-सर्किटिंग व्यवहार है?

+0

मुझे लगता है कि मैंने अपने कोड में किए गए तर्क त्रुटि को * कम से कम * और * बिल्कुल * के संबंध में लोगों को फेंक दिया होगा। मुझे लगता है कि हम अब सबसे अच्छे समाधान पर पहुंचे हैं। – guhou

+1

गणना() केवल संपूर्ण सूची को फिर से भर देगा यदि यह निर्धारित करता है कि उत्तीर्ण आईन्यूमेरेबल आईसीओलेक्शन पर नहीं डाला गया है। तो यदि आप एक सूची उदाहरण, या एक सरणी पास करते हैं, तो यह फिर से चालू नहीं होगा। –

+1

मुझे लगता है कि यह सभी समयपूर्व अनुकूलन है। –

उत्तर

20

यह करना चाहिए:

public static bool ContainsExactlyOneItem<T>(this IEnumerable<T> source) 
{ 
    using (IEnumerator<T> iterator = source.GetEnumerator()) 
    { 
     // Check we've got at least one item 
     if (!iterator.MoveNext()) 
     { 
      return false; 
     } 
     // Check we've got no more 
     return !iterator.MoveNext(); 
    } 
} 

आप आगे इस छिपाना सकता है, लेकिन मैं आपको ऐसा करने के संकेत नहीं करते:

public static bool ContainsExactlyOneItem<T>(this IEnumerable<T> source) 
{ 
    using (IEnumerator<T> iterator = source.GetEnumerator()) 
    { 
     return iterator.MoveNext() && !iterator.MoveNext(); 
    } 
} 

यह की तरह है चाल जो फंकी है, लेकिन शायद उत्पादन कोड में इस्तेमाल नहीं किया जाना चाहिए। यह बस पर्याप्त स्पष्ट नहीं है। तथ्य यह है & & ऑपरेटर की एलएचएस में पक्ष प्रभाव के लिए आवश्यक है कि आरएचएस उचित रूप से काम करने के लिए सिर्फ बुरा है ... जबकि बहुत मज़ा;)

संपादित करें: मैं बस देखा है कि आप के लिए आया था बिल्कुल एक ही चीज़ के साथ लेकिन मनमाने ढंग से लंबाई के लिए। आपका अंतिम रिटर्न स्टेटमेंट गलत है - यह !en.MoveNext() वापस होना चाहिए। CountEquals की एक पुनरावर्ती प्रपत्र (इस का उपयोग नहीं करें कार्यात्मक प्रशंसकों के लिए अब और,:

public static bool CountEquals<T>(this IEnumerable<T> source, int count) 
{ 
    if (source == null) 
    { 
     throw new ArgumentNullException("source"); 
    } 
    if (count < 0) 
    { 
     throw new ArgumentOutOfRangeException("count", 
               "count must not be negative"); 
    } 
    // We don't rely on the optimizations in LINQ to Objects here, as 
    // they have changed between versions. 
    ICollection<T> genericCollection = source as ICollection<T>; 
    if (genericCollection != null) 
    { 
     return genericCollection.Count == count; 
    } 
    ICollection nonGenericCollection = source as ICollection; 
    if (nonGenericCollection != null) 
    { 
     return nonGenericCollection.Count == count; 
    } 
    // Okay, we're finally ready to do the actual work... 
    using (IEnumerator<T> iterator = source.GetEnumerator()) 
    { 
     for (int i = 0; i < count; i++) 
     { 
      if (!iterator.MoveNext()) 
      { 
       return false; 
      } 
     } 
     // Check we've got no more 
     return !iterator.MoveNext(); 
    } 
} 

संपादित करें: यहाँ ICollection/ICollection<T> के लिए एक अच्छे नाम (IMO), तर्क की जाँच और अनुकूलन के साथ एक पूर्ण विधि है , यह केवल यहाँ गिगल्स के लिए है):

public static bool CountEquals<T>(this IEnumerable<T> source, int count) 
{ 
    if (source == null) 
    { 
     throw new ArgumentNullException("source"); 
    } 
    if (count < 0) 
    { 
     throw new ArgumentOutOfRangeException("count", 
               "count must not be negative"); 
    } 
    using (IEnumerator<T> iterator = source.GetEnumerator()) 
    { 
     return IteratorCountEquals(iterator, count); 
    } 
} 

private static bool IteratorCountEquals<T>(IEnumerator<T> iterator, int count) 
{ 
    return count == 0 ? !iterator.MoveNext() 
     : iterator.MoveNext() && IteratorCountEquals(iterator, count - 1); 
} 

संपादित करें: एसक्यूएल के लिए LINQ की तरह कुछ के लिए ध्यान दें कि, आप सरल Count() दृष्टिकोण का उपयोग करना चाहिए - क्योंकि यह है कि यह डेटाबेस के बजाय प्राप्त करने में कठिनाई के बाद किया जाना करने की अनुमति देंगे वास्तविक परिणाम।

+1

हां यह बदसूरत है, और हाँ, यह सुंदर है :-) लेकिन नहीं, जब आप पहली बार यह कोड देखते हैं तो यह बहुत स्पष्ट नहीं है। –

+3

एलओएल @ जोन अपने 'रिटर्न इटरेटर' के लिए। MoveNext() &&! Iterator.MoveNext(); '। मुझसे जवाब "दोनों" है! – mauris

+2

+1 iterator.MoveNext() &&! Iterator.MoveNext() वाह! – onof

7

नहीं है, लेकिन आप एक अपने आप को लिख सकते हैं:

public static bool HasExactly<T>(this IEnumerable<T> source, int count) 
{ 
    if(source == null) 
     throw new ArgumentNullException("source"); 

    if(count < 0) 
     return false; 

    return source.Take(count + 1).Count() == count; 
} 

संपादित करें: स्पष्टीकरण के बाद बिल्कुल करने के लिए कम से कम से बदल दिया है।

एक अधिक सामान्य और कुशल समाधान के लिए (जो केवल 1 प्रगणक और चेक का उपयोग करता है, तो अनुक्रम को लागू करता है ICollection या ICollection<T> जिसमें मामले गणन आवश्यक नहीं है), तो आपको मेरा उत्तर here पर एक नज़र लेने के लिए चाहते हो सकता है, जो आप की सुविधा देता है निर्दिष्ट करें कि आप Exact, AtLeast, या AtMost परीक्षणों की तलाश में हैं या नहीं।

+0

क्या आप अभी भी उस स्निपेट में पूरी सूची का आकलन नहीं कर रहे हैं? ('टेक 'और फिर' गणना 'के कारण) – guhou

+0

मैं * कम से कम * नहीं पूछ रहा हूं, मैं * बिल्कुल * के लिए पूछ रहा हूं। – guhou

+0

@ BleuM937: संपादित। – Ani

0

मुझे विश्वास है कि आप जो खोज रहे हैं वह .Single() है। बिल्कुल एक के अलावा कुछ भी अवैधऑपरेशन अपवाद को फेंक देगा जिसे आप पकड़ सकते हैं।

http://msdn.microsoft.com/nb-no/library/bb155325.aspx

+1

मुझे लगता है कि मैं जो करने की कोशिश कर रहा हूं उसे हासिल किया जा सकता है लेकिन मुझे नियंत्रण प्रवाह के लिए अपवाद फेंकना पसंद नहीं है। – guhou

+1

फिर सिंगलऑर्डफॉल्ट() का उपयोग करें और जांचें कि वापसी मूल्य स्वयं शून्य है या नहीं। – danijels

+0

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

4

seq.Skip(1).Any() आपको बताएगा कि सूची में शून्य या एक तत्व है या नहीं।

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

public static bool LengthEquals<T>(this IEnumerable<T> en, int length) 
    { 
     using (var er = en.GetEnumerator()) 
     { 
      for (int i = 0; i < length; i++) 
      { 
       if (!er.MoveNext()) 
        return false; 
      } 
      return !er.MoveNext(); 
     } 
    } 
+0

हां, मैं बस खुद को देख रहा था। मैं आपकी विधि का नाम पसंद करता हूं, लेकिन मेरे परिवर्तनीय नाम :) - असल में मैंने फैसला किया है कि मैं काउंटरक्वाल्स को सभी के बाद पसंद करता हूं, क्योंकि यह गिनती() विधि के साथ बेहतर होता है :) –

+0

हाँ, काउंटरक्वाल्स मेरे लिए सही लगता है। – SamStephens

1

इस बारे में कैसे?

public static bool CountEquals<T>(this IEnumerable<T> source, int count) { 
    return source.Take(count + 1).Count() == count; 
} 

Take() हम फोन कभी नहीं MoveNextcount+1 से अधिक बार यकीन है कि कर देगा।

मैं ध्यान दें कि ICollection के किसी भी उदाहरण के लिए करना चाहते हैं, मूल कार्यान्वयन source.Count() == count तेजी क्योंकि Count() सिर्फ Count सदस्य को देखने के लिए अनुकूलित है होना चाहिए।

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

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