ফলন রিটার্ন ব্যবহার করে অনুমিত এবং পুনরাবৃত্তি


307

আমার একটি IEnumerable<T>পদ্ধতি রয়েছে যা আমি একটি ওয়েব ফর্ম পৃষ্ঠাতে নিয়ন্ত্রণগুলি খুঁজতে ব্যবহার করি।

পদ্ধতিটি পুনরাবৃত্তিযোগ্য এবং yield returnপুনরাবৃত্ত কলটির মানটি ফিরিয়ে দিলে আমার যে প্রকারটি চান তা ফেরত দিতে আমার কিছু সমস্যা হচ্ছে ।

আমার কোডটি নিম্নরূপ দেখায়:

    public static IEnumerable<Control> 
                               GetDeepControlsByType<T>(this Control control)
    {
        foreach(Control c in control.Controls)
        {
            if (c is T)
            {
                yield return c;
            }

            if(c.Controls.Count > 0)
            {
                yield return c.GetDeepControlsByType<T>();
            }
        }
    }

এটি বর্তমানে "এক্সপ্রেশনের ধরণকে রূপান্তর করতে পারে না" ত্রুটি ছুড়ে ফেলে। তবে যদি এই পদ্ধতিটি টাইপ করে IEnumerable<Object>, কোড তৈরি করে তবে ভুল টাইপটি আউটপুটটিতে ফিরে আসে।

yield returnপুনরাবৃত্তি ব্যবহার করার সময় কি কোনও উপায় আছে ?


1
stackoverflow.com/questions/1815497/... : "enumerating যে সংগ্রহগুলি মজ্জাগতভাবে IEnumerable নয়" থ্রেডে "mrydengrens" উত্তর লিংক তার নমুনা কোডটি এরিক লিপার্টের একটি ব্লগ নিবন্ধের উপর ভিত্তি করে আপনাকে দেখায় যে কীভাবে লিনকের সাথে পুনরাবৃত্ত গণনার ক্ষেত্রে স্ট্যাকগুলি ব্যবহার করতে হবে, এটি সম্ভবত পুনরাবৃত্তিকারীদের দ্বারা ব্যয়বহুল মেমরির ব্যবহার এড়ানো যায়। ইমো খুব দরকারী!
বিল ডাব্লু

BTW। if(c.Controls.Count > 0)-> if(c.Controls.Any()), বিশেষত যদি আপনি ভাল
ফলনও করেন

আমি মনে করি না যে এই ক্ষেত্রে ফলন দিয়ে উপকৃত হয়। সম্পূর্ণতার জন্য আমি ছাড়াই একটি বাস্তবায়ন সরবরাহ করেছি yield। দয়া করে নীচে দেখুন :) এবং এটি একটি ওয়ান-
লাইনারও রয়েছে

yield returnপুনরাবৃত্তামূলক ক্রিয়াগুলি এড়াতে আপনার সতর্ক হওয়া উচিত , মেমরির ব্যবহারটি বিস্ফোরকভাবে স্কেল করে। স্ট্যাকওভারফ্লো.com
কর্নেল আতঙ্ক

উত্তর:


485

একটি পদ্ধতির অভ্যন্তরে ফিরে আসে IEnumerable<T>, yield returnফিরে আসতে হবে T, একটি নয় IEnumerable<T>

প্রতিস্থাপন করা

yield return c.GetDeepControlsByType<T>();

সঙ্গে:

foreach (var x in c.GetDeepControlsByType<T>())
{
  yield return x;
}

98

পুনরাবৃত্তি কল দ্বারা উত্পন্ন আইটেমগুলির প্রতিটি আপনার উত্পাদন করতে হবে:

public static IEnumerable<Control> GetDeepControlsByType<T>(this Control control)
{
    foreach(Control c in control.Controls)
    {
        if (c is T)
        {
            yield return c;
        }

        if(c.Controls.Count > 0)
        {
            foreach (Control control in c.GetDeepControlsByType<T>())
            {
                yield return control;
            }
        }
    }
}

মনে রাখবেন যে এইভাবে পুনরাবৃত্তি করতে ব্যয় করতে হবে - আপনি প্রচুর পুনরুক্তি তৈরির কাজ শেষ করবেন, যা আপনার যদি সত্যিকারের গভীর নিয়ন্ত্রণ গাছ থাকে তবে পারফরম্যান্স ইস্যু তৈরি করতে পারে। আপনি যদি তা এড়াতে চান, তবে কেবলমাত্র একটি পুনরুক্তি (রাষ্ট্রীয় মেশিন) তৈরি হয়েছে তা নিশ্চিত করার জন্য আপনাকে মূলত পদ্ধতিটির মধ্যে নিজেকে পুনরাবৃত্তি করতে হবে। আরও বিশদ এবং একটি নমুনা বাস্তবায়নের জন্য এই প্রশ্নটি দেখুন - তবে এটি স্পষ্টতই জটিলতার একটি নির্দিষ্ট পরিমাণও যুক্ত করে।


2
আমি এটা সম্পর্কে ফলনশীল জন উল্লেখ করা হয়েছে থ্রেড বিস্ময়কর যে এটি c.Controls.Count > 0বনাম .Any():)
tymtam

@ টাইমেক আসলে এটি লিঙ্কিত উত্তরে উল্লিখিত হয়েছে।

28

জোন স্কিটি এবং কর্নেল প্যানিক যেমন তাদের উত্তরে নোট করেছে, yield returnগাছটি খুব গভীর হয় তবে পুনরাবৃত্তির পদ্ধতিতে ব্যবহার করা পারফরম্যান্সের সমস্যার কারণ হতে পারে।

এখানে একটি জেনেরিক অ-রিকার্সিভ এক্সটেনশন পদ্ধতি যা গাছের ক্রমগুলির গভীরতার প্রথম ট্র্যাভারসাল সম্পাদন করে:

public static IEnumerable<TSource> RecursiveSelect<TSource>(
    this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> childSelector)
{
    var stack = new Stack<IEnumerator<TSource>>();
    var enumerator = source.GetEnumerator();

    try
    {
        while (true)
        {
            if (enumerator.MoveNext())
            {
                TSource element = enumerator.Current;
                yield return element;

                stack.Push(enumerator);
                enumerator = childSelector(element).GetEnumerator();
            }
            else if (stack.Count > 0)
            {
                enumerator.Dispose();
                enumerator = stack.Pop();
            }
            else
            {
                yield break;
            }
        }
    }
    finally
    {
        enumerator.Dispose();

        while (stack.Count > 0) // Clean up in case of an exception.
        {
            enumerator = stack.Pop();
            enumerator.Dispose();
        }
    }
}

এরিক লিপার্টের সমাধানের বিপরীতে , রিকার্সসিলেক্ট গণনাকারীদের সাথে সরাসরি কাজ করে যাতে এর বিপরীতে কল করার প্রয়োজন হয় না (যা স্মৃতিতে পুরো ক্রমটি বাফার করে)।

রিকার্সসিলেক্ট ব্যবহার করে ওপির মূল পদ্ধতিটি কেবল এভাবে লেখা যেতে পারে:

public static IEnumerable<Control> GetDeepControlsByType<T>(this Control control)
{
    return control.Controls.RecursiveSelect(c => c.Controls).Where(c => c is T);
}

এই (দুর্দান্ত) কোডটি কাজ করার জন্য, আমাকে 'অফটাইপ ব্যবহার করতে হবে কন্ট্রোলক্ল্যাকশনটি আইনামেবল ফর্মে যেতে; উইন্ডোজ ফর্মগুলিতে একটি কন্ট্রোল সংগ্রহটি গণনীয় নয়: রিটার্ন নিয়ন্ত্রণ );
বিল ডব্লু

17

অন্যরা আপনাকে সঠিক উত্তর দিয়েছিল তবে আমি মনে করি না যে আপনার ক্ষেত্রে ফলন হচ্ছে benefits

এখানে একটি স্নিপেট যা ফলন ছাড়াই এটি অর্জন করে।

public static IEnumerable<Control> GetDeepControlsByType<T>(this Control control)
{
   return control.Controls
                 .Where(c => c is T)
                 .Concat(control.Controls
                                .SelectMany(c =>c.GetDeepControlsByType<T>()));
}

2
yieldপাশাপাশি লিনকিউ ব্যবহার করে না ? ;)
ফিলিপ এম

এটি চতুর। আমি অতিরিক্ত foreachলুপ দ্বারা সর্বদা বিরক্ত হই । এখন আমি খাঁটি ফাংশনাল প্রোগ্রামিং দিয়ে এটি করতে পারি!
jsuddsjr

1
আমি পাঠ্যযোগ্যতার দিক থেকে এই সমাধানটি পছন্দ করি তবে এটি ফলন ব্যবহারের ক্ষেত্রে পুনরাবৃত্তকারীদের সাথে একই পারফরম্যান্স সমস্যার মুখোমুখি। @PhilippM: যাচাইকৃত যে LINQ ব্যবহারসমূহ উত্পাদ referencesource.microsoft.com/System.Core/R/...
হারমান

একটি দুর্দান্ত সমাধানের জন্য থাম্ব আপ।
টোমর ডব্লিউ

12

আপনাকে দ্বিতীয় সেকেন্ডে এনুমুলেটর থেকে নয়, বরং আপনাকে গণকের কাছ থেকে আইটেমগুলি ফিরিয়ে দিতে হবেyield return

public static IEnumerable<Control> GetDeepControlsByType<T>(this Control control)
{
    foreach (Control c in control.Controls)
    {
        if (c is T)
        {
            yield return c;
        }

        if (c.Controls.Count > 0)
        {
            foreach (Control ctrl in c.GetDeepControlsByType<T>())
            {
                yield return ctrl;
            }
        }
    }
}

9

আমি মনে করি আপনাকে অঙ্কের প্রতিটি নিয়ন্ত্রণের জন্য ফিরে আসতে হবে।

    public static IEnumerable<Control> GetDeepControlsByType<T>(this Control control)
    {
        foreach (Control c in control.Controls)
        {
            if (c is T)
            {
                yield return c;
            }

            if (c.Controls.Count > 0)
            {
                foreach (Control childControl in c.GetDeepControlsByType<T>())
                {
                    yield return childControl;
                }
            }
        }
    }

8

সেরেডেনস্কির বাক্য গঠনটি সঠিক, তবে yield returnপুনরাবৃত্ত ক্রিয়াকলাপগুলি এড়াতে আপনার সতর্ক হওয়া উচিত কারণ এটি স্মৃতি ব্যবহারের জন্য একটি বিপর্যয়। Https://stackoverflow.com/a/3970171/284795 দেখুন এটি গভীরতার সাথে বিস্ফোরকভাবে স্কেল করে (একই অ্যাপ্লিকেশনটিতে 10% মেমরি ব্যবহার করা হয়েছিল)।

একটি সহজ সমাধান হ'ল একটি তালিকা ব্যবহার করা এবং এটি পুনরাবৃত্তি https://codereview.stackexchange.com/a/5651/754 দিয়ে পাস করা

/// <summary>
/// Append the descendents of tree to the given list.
/// </summary>
private void AppendDescendents(Tree tree, List<Tree> descendents)
{
    foreach (var child in tree.Children)
    {
        descendents.Add(child);
        AppendDescendents(child, descendents);
    }
}

অন্যথায় আপনি recursive কল নির্মূল করার একটি স্ট্যাক এবং একটি লুপ ব্যবহার করতে পারে https://codereview.stackexchange.com/a/5661/754


0

যদিও সেখানে অনেকগুলি ভাল উত্তর রয়েছে, আমি এখনও যুক্ত করব যে একই জিনিসটি সম্পাদন করার জন্য লিনকিউ পদ্ধতি ব্যবহার করা সম্ভব,।

উদাহরণস্বরূপ, ওপির মূল কোডটি আবার লিখিত হতে পারে:

public static IEnumerable<Control> 
                           GetDeepControlsByType<T>(this Control control)
{
   return control.Controls.OfType<T>()
          .Union(control.Controls.SelectMany(c => c.GetDeepControlsByType<T>()));        
}

একই পদ্ধতির ব্যবহার করে একটি সমাধান তিন বছর আগে পোস্ট করা হয়েছিল ।
পরিবেশন করুন

@Servy যদিও অনুরূপ (যা BTW আমি সব উত্তর মধ্যে মিস ... যখন এই উত্তরটি লেখার), এটা, এখনও ভিন্ন যেমন ফিল্টারের ব্যবহার .OfType <> এবং .Union ()
yoel halb

2
OfTypeসত্যিই একটি meainingful পার্থক্য নেই। সর্বাধিক গৌণ স্টাইলিস্টিক পরিবর্তন। একটি নিয়ন্ত্রণ একাধিক নিয়ন্ত্রণের শিশু হতে পারে না, সুতরাং বিবর্তিত গাছটি ইতিমধ্যে অপ্রতুল। এর Unionপরিবর্তে ব্যবহার করা Concatঅনাকাঙ্খিতভাবে ইতিমধ্যে অনন্য হওয়ার গ্যারান্টিযুক্ত ক্রমটির স্বতন্ত্রতা যাচাই করা এবং এটি একটি উদ্দেশ্যমূলক ডাউনগ্রেড।
পরিবেশন করুন
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.