पैदावार वापसी के साथ सभी enumerables एक बार में लौटें; बिना लूपिंग के


164

मेरे पास एक कार्ड के लिए सत्यापन त्रुटियां प्राप्त करने के लिए निम्नलिखित फ़ंक्शन हैं। मेरा प्रश्न गेटएयर से निपटने से संबंधित है। दोनों विधियों में समान रिटर्न प्रकार है IEnumerable<ErrorInfo>

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    var errors = GetMoreErrors(card);
    foreach (var e in errors)
        yield return e;

    // further yield returns for more validation errors
}

क्या GetMoreErrorsउनके माध्यम से गणना करने के बिना सभी त्रुटियों को वापस करना संभव है ?

इसके बारे में सोचना शायद एक बेवकूफ सवाल है, लेकिन मैं यह सुनिश्चित करना चाहता हूं कि मैं गलत नहीं हूं।


मैं खुश हूँ (और उत्सुक!) अधिक पैदावार वापसी के सवाल देखने के लिए - मैं खुद इसे काफी नहीं समझता। बेवकूफ सवाल नहीं!
जोशजोर्डन

क्या है GetCardProductionValidationErrorsFor?
एंड्रयू हरे

4
GetMoreErrors (कार्ड) के साथ गलत क्या है ; ?
सैम केसर

10
@ सैम: "अधिक सत्यापन त्रुटियों के लिए आगे की पैदावार"
जॉन स्कीट

1
गैर-अस्पष्ट भाषा के दृष्टिकोण से, एक मुद्दा यह है कि विधि यह नहीं जान सकती है कि क्या कुछ भी है जो T और IEnumerable <T> दोनों को लागू करता है। तो आपको उपज में एक अलग निर्माण की आवश्यकता है। उस ने कहा, यह सुनिश्चित करना अच्छा होगा कि ऐसा करने का एक तरीका है। पैदावार वापसी उपज फू, शायद, जहां फू लागू करता है IEnumerable <T>?
विलियम जॉकस

जवाबों:


141

यह निश्चित रूप से एक बेवकूफ सवाल नहीं है, और यह कुछ ऐसा है जो एफ # एक एकल आइटम के yield!लिए एक पूरे संग्रह के लिए समर्थन करता है yield। (यह पूंछ पुनरावृत्ति के संदर्भ में बहुत उपयोगी हो सकता है ...)

दुर्भाग्य से यह C # में समर्थित नहीं है।

हालाँकि, यदि आपके पास लौटाने वाले प्रत्येक तरीके हैं, तो आप अपने कोड को सरल बनाने के लिए IEnumerable<ErrorInfo>उपयोग कर सकते हैं Enumerable.Concat:

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    return GetMoreErrors(card).Concat(GetOtherErrors())
                              .Concat(GetValidationErrors())
                              .Concat(AnyMoreErrors())
                              .Concat(ICantBelieveHowManyErrorsYouHave());
}

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

आमतौर पर यह महत्वपूर्ण नहीं है, लेकिन यह समझने लायक है कि कब क्या होने वाला है।


3
वेस डायर ने इस पैटर्न का उल्लेख करते हुए एक दिलचस्प लेख लिखा है। blogs.msdn.com/wesdyer/archive/2007/03/23/...
JohannesH

1
राहगीरों के लिए मामूली सुधार - यह System.Linq.Enumeration.Concat <> (पहला, दूसरा) है। नहीं IEnumeration.Concat ()।
Redcalx

@-लोकेशन: मुझे यकीन नहीं है कि आपका क्या मतलब है। यह निश्चित रूप से एन्युमरेशन के बजाय एन्युमरेबल है। क्या आप अपनी टिप्पणी स्पष्ट कर सकते हैं?
जॉन स्कीट

@ जोन स्कीट - वास्तव में आपका क्या मतलब है कि यह तुरंत तरीकों को बुलाएगा? मैंने एक परीक्षण चलाया और ऐसा लगता है कि यह विधि कॉल को पूरी तरह से स्थगित कर रहा है जब तक कि कुछ वास्तव में पुनरावृत्त न हो। यहाँ कोड: pastebin.com/0kj5QtfD
स्टीवन ऑक्सले

5
@ उत्तर: नहीं। यह विधियों को बुला रहा है - लेकिन आपके मामले में GetOtherErrors()(आदि) उनके परिणामों को टाल रहा है (जैसा कि वे पुनरावृत्त ब्लॉकों का उपयोग करके लागू किया गया है)। एक नई सरणी या ऐसा कुछ वापस करने के लिए उन्हें बदलने का प्रयास करें, और आप देखेंगे कि मेरा क्या मतलब है।
जॉन स्कीट

26

आप इस तरह के सभी त्रुटि स्रोत स्थापित कर सकते हैं (जॉन स्कीट के उत्तर से उधार ली गई विधि के नाम)।

private static IEnumerable<IEnumerable<ErrorInfo>> GetErrorSources(Card card)
{
    yield return GetMoreErrors(card);
    yield return GetOtherErrors();
    yield return GetValidationErrors();
    yield return AnyMoreErrors();
    yield return ICantBelieveHowManyErrorsYouHave();
}

आप एक ही समय में उन पर पुनरावृति कर सकते हैं।

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    foreach (var errorSource in GetErrorSources(card))
        foreach (var error in errorSource)
            yield return error;
}

वैकल्पिक रूप से आप त्रुटि स्रोतों को समतल कर सकते हैं SelectMany

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    return GetErrorSources(card).SelectMany(e => e);
}

विधियों के निष्पादन GetErrorSourcesमें भी देरी होगी।


17

मैं एक त्वरित yield_स्निपेट के साथ आया :

उपज_ स्निप उपयोग एनीमेशन

यहाँ स्निपेट XML है:

<?xml version="1.0" encoding="utf-8"?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <CodeSnippet Format="1.0.0">
    <Header>
      <Author>John Gietzen</Author>
      <Description>yield! expansion for C#</Description>
      <Shortcut>yield_</Shortcut>
      <Title>Yield All</Title>
      <SnippetTypes>
        <SnippetType>Expansion</SnippetType>
      </SnippetTypes>
    </Header>
    <Snippet>
      <Declarations>
        <Literal Editable="true">
          <Default>items</Default>
          <ID>items</ID>
        </Literal>
        <Literal Editable="true">
          <Default>i</Default>
          <ID>i</ID>
        </Literal>
      </Declarations>
      <Code Language="CSharp"><![CDATA[foreach (var $i$ in $items$) yield return $i$$end$;]]></Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

2
यह सवाल का जवाब कैसे है?
इयान केम्प

1
@, यह है कि आपको C # में नेस्टेड यील्ड रिटर्न कैसे करना है। yield!F # की तरह कोई भी नहीं है ।
जॉन गीत्ज़ेन

यह प्रश्न का उत्तर नहीं है
दिव्यांग ४४ 10१

8

मुझे आपके फ़ंक्शन में कुछ भी गलत नहीं दिख रहा है, मैं कहूंगा कि यह वही है जो आप चाहते हैं।

अंतिम बार एन्यूमरेशन में एक तत्व को लौटाने के रूप में यील्ड के बारे में सोचें जो कि यह आह्वान किया गया है, इसलिए जब आपके पास इसे इस तरह से फॉरेस्ट लूप में रखा जाता है, तो हर बार जब इसे लागू किया जाता है तो यह 1 तत्व देता है। आप परिणाम को फ़िल्टर करने के लिए अपने फ़ॉरेस्ट में सशर्त विवरण डालने की क्षमता रखते हैं। (केवल अपने अपवर्जन मानदंडों पर पैदावार नहीं करके)

यदि आप बाद की पैदावार को बाद में विधि में जोड़ते हैं, तो यह गणना में 1 तत्व जोड़ना जारी रखेगा, जिससे चीजों को करना संभव हो ...

public IEnumerable<string> ConcatLists(params IEnumerable<string>[] lists)
{
  foreach (IEnumerable<string> list in lists)
  {
    foreach (string s in list)
    {
      yield return s;
    }
  }
}

4

मुझे आश्चर्य है कि किसी ने भी IEnumerable<IEnumerable<T>>इस कोड को अपने आस्थगित निष्पादन को बनाए रखने के लिए एक सरल एक्सटेंशन विधि की सिफारिश करने के लिए नहीं सोचा है । मैं कई कारणों से आस्थगित निष्पादन का प्रशंसक रहा हूं, उनमें से एक यह है कि विशाल-विशाल संयुग्मों के लिए भी स्मृति पदचिह्न छोटा है।

public static class EnumearbleExtensions
{
    public static IEnumerable<T> UnWrap<T>(this IEnumerable<IEnumerable<T>> list)
    {
        foreach(var innerList in list)
        {
            foreach(T item in innerList)
            {
                yield return item;
            }
        }
    }
}

और आप इसे इस तरह से अपने मामले में इस्तेमाल कर सकते हैं

private static IEnumerable<ErrorInfo> GetErrors(Card card)
{
    return DoGetErrors(card).UnWrap();
}

private static IEnumerable<IEnumerable<ErrorInfo>> DoGetErrors(Card card)
{
    yield return GetMoreErrors(card);

    // further yield returns for more validation errors
}

इसी तरह, आप रैपर फंक्शन को दूर कर सकते हैं DoGetErrorsऔर सिर्फ UnWrapकॉलसाइट में जा सकते हैं।


2
संभवतः किसी ने विस्तार विधि के बारे में नहीं सोचा था क्योंकि DoGetErrors(card).SelectMany(x => x)वही करता है और आस्थगित व्यवहार को संरक्षित करता है। जो वास्तव में आदम अपने जवाब में सुझाता है ।
हुइसेन्ट्रक्टव

3

हाँ सभी त्रुटियों को एक बार में वापस करना संभव है। बस एक वापसी List<T>याReadOnlyCollection<T>

लौटकर ए IEnumerable<T> आप किसी चीज़ का एक क्रम लौटा रहे हैं। सतह पर जो संग्रह को वापस करने के लिए समान लग सकता है, लेकिन कई अंतर हैं, आपको ध्यान में रखना चाहिए।

संग्रह

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

दृश्यों

  • प्रगणित किया जा सकता है - और यह बहुत अधिक है जो हम निश्चित रूप से कह सकते हैं।
  • एक लौटा हुआ क्रम ही संशोधित नहीं किया जा सकता है।
  • प्रत्येक तत्व को अनुक्रम के माध्यम से चलाने के भाग के रूप में बनाया जा सकता है (अर्थात IEnumerable<T>आलसी मूल्यांकन की अनुमति देता है, वापस List<T>नहीं करता है)।
  • एक अनुक्रम अनंत हो सकता है और इस तरह यह कॉल करने वाले को यह तय करने के लिए छोड़ देता है कि कितने तत्वों को वापस किया जाना चाहिए।

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

मैं सहमत हूं, और अगर आपको यह आभास हो गया कि मैंने कहा कि संग्रह वापस करना हमेशा एक अच्छा विचार है, तो आप मेरी बात को याद करेंगे। मैं इस तथ्य को उजागर करने की कोशिश कर रहा था कि एक संग्रह को वापस करने और एक अनुक्रम को वापस करने के बीच अंतर हैं। मैं इसे स्पष्ट करने की कोशिश करूंगा।
ब्रायन रासमुसेन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.