লিঙ্কে ব্যাচগুলি তৈরি করুন


113

কেউ কি লিন্কে একটি নির্দিষ্ট আকারের ব্যাচ তৈরির কোনও উপায় প্রস্তাব করতে পারেন?

আদর্শভাবে আমি কিছু কনফিগারযোগ্য পরিমাণের অংশগুলিতে অপারেশন করতে সক্ষম হতে চাই।

উত্তর:


120

আপনার কোনও কোড লেখার দরকার নেই। মোরলিংকিউ ব্যাচ পদ্ধতিটি ব্যবহার করুন , যা উত্স ক্রমটিকে আকারের বালতিতে ব্যাচ করে (আপনি ইনস্টল করতে পারেন এমন নুগেট প্যাকেজ হিসাবে মোরলিংকিউ উপলব্ধ):

int size = 10;
var batches = sequence.Batch(size);

যা প্রয়োগ করা হয়:

public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
                  this IEnumerable<TSource> source, int size)
{
    TSource[] bucket = null;
    var count = 0;

    foreach (var item in source)
    {
        if (bucket == null)
            bucket = new TSource[size];

        bucket[count++] = item;
        if (count != size)
            continue;

        yield return bucket;

        bucket = null;
        count = 0;
    }

    if (bucket != null && count > 0)
        yield return bucket.Take(count).ToArray();
}

4
আইটেম প্রতি 4 বাইট ভয়ঙ্কর অভিনয় করে ? আপনার কি কিছু পরীক্ষা আছে যা দেখায় ভয়াবহ অর্থ কী? আপনি যদি কয়েক মিলিয়ন আইটেম মেমোরিতে লোড করে থাকেন তবে আমি এটি করব না। সার্ভার-সাইড পেজিং ব্যবহার করুন
সের্গেই বেরেজোভস্কি

4
আমি আপনাকে আপত্তি জানাতে চাইছি না, তবে এর চেয়ে সহজ সমাধান রয়েছে যা মোটেও জমে না। তদতিরিক্ত এটি অস্তিত্বহীন উপাদানগুলির জন্য এমনকি স্থান বরাদ্দ করবে:Batch(new int[] { 1, 2 }, 1000000)
নিক হোয়ালি

8
@ নিক ওহলে ভাল, আপনার সাথে একমত যে অতিরিক্ত স্থান বরাদ্দ হবে, তবে বাস্তব জীবনে আপনার সাধারণত বিপরীত পরিস্থিতি থাকে - ১০০ টি আইটেমের তালিকা যা 50 এর ব্যাচে যেতে হবে :)
সের্গেই বেরেজভস্কি

4
হ্যাঁ পরিস্থিতিটি সাধারণত অন্যভাবে হওয়া উচিত তবে বাস্তব জীবনে এগুলি ব্যবহারকারীর ইনপুট হতে পারে।
নিক তিমি

9
এটি একটি নিখুঁত সূক্ষ্ম সমাধান। বাস্তব জীবনে আপনি: ব্যবহারকারীর ইনপুটকে বৈধতা দিন, ব্যাচগুলিকে পুরো সংগ্রহ হিসাবে আইটেমগুলি বিবেচনা করুন (যা আইটেমগুলি যেভাবেই জমা করে) এবং প্রায়শই সমান্তরালভাবে ব্যাচগুলি প্রক্রিয়াজাত করে (যা পুনরুক্তি পদ্ধতির দ্বারা সমর্থিত নয়, এবং যদি আপনি না জেনেন তবে একটি অভদ্র বিস্ময় হবে বাস্তবায়ন বিশদ)।
মাইকেল পেতিটো

93
public static class MyExtensions
{
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items,
                                                       int maxItems)
    {
        return items.Select((item, inx) => new { item, inx })
                    .GroupBy(x => x.inx / maxItems)
                    .Select(g => g.Select(x => x.item));
    }
}

এবং ব্যবহার হবে:

List<int> list = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

foreach(var batch in list.Batch(3))
{
    Console.WriteLine(String.Join(",",batch));
}

আউটপুট:

0,1,2
3,4,5
6,7,8
9

আমার জন্য নিখুঁত কাজ করেছেন
ফানমাটারগুলি 6:58

16
একবার GroupByগণনা শুরু হলে , এটির উত্সটি পুরোপুরি গণনা করতে হবে না? এটি উত্সটির অলস মূল্যায়ন হারায় এবং এইভাবে, কিছু ক্ষেত্রে, ব্যাচিংয়ের সমস্ত সুবিধা!
এরিক

4
বাহ, ধন্যবাদ, আপনি আমাকে উন্মাদনা থেকে বাঁচিয়েছেন। খুব ভাল কাজ করে
রিয়ান ডি ল্যাঞ্জ

4
@ এরিক যেমন উল্লেখ করেছেন, এই পদ্ধতিটি সম্পূর্ণরূপে এর উত্সকে গণনা করেছে তবে এটি দেখতে দুর্দান্ত
লাগলেও

4
এটি করুন - এটি পুরোপুরি উপযুক্ত যখন আপনাকে পারফরম্যান্ট প্রসেসিংয়ের জন্য জিনিসগুলির ছোট ছোট ব্যাচে বিভক্ত করার প্রয়োজন হয়। বিকল্পটি এমন এক লুপের সন্ধান যা আপনি ম্যানুয়ালি ব্যাচগুলি ভেঙে ফেলেন and এবং এখনও পুরো উত্সটি দিয়ে যান।
স্টিংজি জ্যাক

35

যদি আপনি sequenceএকটি হিসাবে সংজ্ঞায়িত দিয়ে শুরু করেন IEnumerable<T>এবং আপনি জানেন যে এটি নিরাপদে একাধিকবার গণনা করা যেতে পারে (উদাহরণস্বরূপ এটি একটি অ্যারে বা একটি তালিকা হিসাবে) তবে আপনি কেবল ব্যাচগুলির উপাদানগুলি প্রক্রিয়া করার জন্য এই সাধারণ প্যাটার্নটি ব্যবহার করতে পারেন:

while (sequence.Any())
{
    var batch = sequence.Take(10);
    sequence = sequence.Skip(10);

    // do whatever you need to do with each batch here
}


5
@ দেভহক: তা। দ্রষ্টব্য, তবে, সেই কর্মক্ষমতা বড় (আর) সংগ্রহের ক্ষেত্রে তাত্পর্যপূর্ণভাবে ক্ষতিগ্রস্থ হবে
রবিআইআইআই

28

উপরের সমস্তগুলি বড় ব্যাচ বা লো মেমরি স্পেসের সাথে ভয়ঙ্করভাবে সঞ্চালন করে। পাইপলাইনের জন্য আমার নিজের লিখতে হবে (কোথাও কোনও আইটেম জমে না সেদিকে লক্ষ্য করুন):

public static class BatchLinq {
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size) {
        if (size <= 0)
            throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");

        using (IEnumerator<T> enumerator = source.GetEnumerator())
            while (enumerator.MoveNext())
                yield return TakeIEnumerator(enumerator, size);
    }

    private static IEnumerable<T> TakeIEnumerator<T>(IEnumerator<T> source, int size) {
        int i = 0;
        do
            yield return source.Current;
        while (++i < size && source.MoveNext());
    }
}

সম্পাদনা: এই পদ্ধতির সাথে জ্ঞাত সমস্যাটি হ'ল পরবর্তী ব্যাচে যাওয়ার আগে প্রতিটি ব্যাচকে অবশ্যই গণনা করা এবং পুরোপুরি গণনা করতে হবে। উদাহরণস্বরূপ এটি কাজ করে না:

//Select first item of every 100 items
Batch(list, 100).Select(b => b.First())

4
উপরের পোস্ট করা এলটিবিএল রুটিনটি আইটেম সংশ্লেষও সম্পাদন করে না।
নিওন্টাপির

4
পুনঃটুইট করেছেন একটি মুদ্রা বাছাইয়ের মেশিন যা আপনাকে প্রথমে নিকেলস দেয়, তারপরে ডাইমস, আপনাকে আরও কোনও নিক নেই বলে নিশ্চিত করার জন্য আপনাকে প্রথমে প্রতি একক মুদ্রাটি পরীক্ষা করা উচিত insp
নিক তিমি

4
আহ্ আহা, আমি এই কোডটি ছিনিয়ে নেওয়ার সময় আপনার সম্পাদনা নোটটি মিস করেছি। এটি গণনা করার জন্য কিছু সময় নিয়েছে কেন আন-গণিত ব্যাচগুলির উপর পুনরাবৃত্তি আসলে সম্পূর্ণ আসল সংগ্রহকে গণনা করেছিল (!!!), এক্স ব্যাচ সরবরাহ করে, প্রত্যেকটিতে 1 টি আইটেম গণনা করা হয়েছে (যেখানে X মূল সংগ্রহের আইটেমের সংখ্যা)।
ইলি

4
@ নিক হোয়ালি যদি আমি আপনার কোড অনুসারে আইইনামেবল <আইনিউবার্য <টি>> ফলাফলের গণনা () করি তবে এটি ভুল উত্তর দেয়, এটি উপাদানগুলির মোট সংখ্যা দেয়, যখন প্রত্যাশিত মোট ব্যাচের তৈরি সংখ্যা। মোরেলিংক ব্যাচের কোডের ক্ষেত্রে এটি হয় না
মৃণাল

4
@ জোহানজ্যাব্রোস্কি - এখানে একটি দ্রুত বক্তব্য
ম্যাট মুরেল

24

এটি সম্পূর্ণ অলস, লো ওভারহেড, ব্যাচের এক-ফাংশন বাস্তবায়ন যা কোনও জমে না। এরিকরোলারের সাহায্যে নিক তিমির সমাধান (এবং এতে সমস্যার সমাধান করে) এর ভিত্তিতে।

ইন্টেরেশন সরাসরি অন্তর্নিহিত আইমনামেবল থেকে আসে, সুতরাং উপাদানগুলি অবশ্যই কঠোর ক্রমে গণনা করতে হবে এবং একাধিকবার অ্যাক্সেস করা উচিত নয়। যদি কিছু উপাদান কোনও অভ্যন্তরীণ লুপে গ্রাস না করা হয় তবে সেগুলি ফেলে দেওয়া হয় (এবং সেভ করা পুনরুত্থকটির মাধ্যমে আবার এগুলিতে অ্যাক্সেসের চেষ্টা করা নিক্ষেপ করবে InvalidOperationException: Enumeration already finished.)।

আপনি । নেট ফিডল একটি সম্পূর্ণ নমুনা পরীক্ষা করতে পারেন ।

public static class BatchLinq
{
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
    {
        if (size <= 0)
            throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
        using (var enumerator = source.GetEnumerator())
            while (enumerator.MoveNext())
            {
                int i = 0;
                // Batch is a local function closing over `i` and `enumerator` that
                // executes the inner batch enumeration
                IEnumerable<T> Batch()
                {
                    do yield return enumerator.Current;
                    while (++i < size && enumerator.MoveNext());
                }

                yield return Batch();
                while (++i < size && enumerator.MoveNext()); // discard skipped items
            }
    }
}

4
এটি এখানে একমাত্র সম্পূর্ণ অলস বাস্তবায়ন। পাইথন ইটারটুলসের সাথে সামঞ্জস্যপূর্ণ rou গোষ্ঠী দ্বারা বাস্তবায়ন।
এরিক রোলার

4
আপনি doneসর্বদা কল করার e.Count()পরে চেকটি অপসারণ করতে পারেন yield return e। আপনি অনির্ধারিত আচরণ ডাকা না BatchInner লুপ পুনরায় সাজাতে হবে source.Currentযদি i >= size। এটি BatchInnerপ্রতিটি ব্যাচের জন্য নতুন বরাদ্দের প্রয়োজনীয়তা দূর করবে ।
এরিক রোলার

4
আপনি ঠিক বলেছেন, প্রতিটি ব্যাচের অগ্রগতি সম্পর্কে আপনাকে এখনও তথ্য ক্যাপচার করতে হবে। আপনি যদি প্রতিটি ব্যাচ থেকে ২ য় আইটেম পাওয়ার চেষ্টা করেন তবে আমি আপনার কোডটিতে একটি বাগ খুঁজে পেয়েছি: বাগ ফিডল । পৃথক শ্রেণি ছাড়াই স্থির বাস্তবায়ন (সি # 7 ব্যবহার করে) এখানে রয়েছে: ফিক্সড ফিডল । নোট করুন যে আমি প্রত্যাশা করি যে সিএলআর এখনও ভেরিয়েবল ক্যাপচার করার জন্য লুপ প্রতি একবার স্থানীয় ফাংশন তৈরি করবে iতাই এটি পৃথক শ্রেণির সংজ্ঞা নির্ধারণের চেয়ে অগত্যা আরও কার্যকর নয়, তবে এটি আমার মনে হয় যে এটি একটু পরিচ্ছন্ন।
এরিক রোলার

4
আমি সিস্টেমটির বিরুদ্ধে বেনমার্কডটনেট ব্যবহার করে এই সংস্করণটিকে বেঞ্চমার্ক করেছি eআপনিফিকেশন.লিংক.ইনিউমারেবলএক্স.ফুফার এবং আপনার প্রয়োগটি সুরক্ষার ঝুঁকিতে ২-৩ দ্রুত ছিল 3-4 অভ্যন্তরীণভাবে, এনিউমারেবলএক্স.ফুফার তালিকাটির একটি সারি বরাদ্দ করেছে <T> github.com/dotnet/reactive/blob/…
জন জাব্রোস্কি

4
আপনি যদি এর একটি বাফার সংস্করণ চান তবে আপনি এটি করতে পারেন: পাবলিক স্ট্যাটিক আইনিউবারেবল <IReadOnlyList <T>> ব্যাচবফার্ড <টি> (এই আইনিমেয়্যরিটি <টি> উত্স, অন্তর্ আকার) => ব্যাচ (উত্স, আকার) নির্বাচন করুন (অংশ = > (IReadOnlyList <T>) খণ্ড.ToList ()); IReadOnlyList <T> এর ব্যবহারটি আউটপুটটি ক্যাশে হওয়ার ইঙ্গিত দেওয়ার জন্য। আপনি তার পরিবর্তে আইনামিউরেবল <আইনিম্যারেবল <T>> রাখতে পারেন।
gfache

11

আমি অবাক হয়েছি কেন কেন কেউ লুপ সমাধানের জন্য কোনও পুরানো স্কুল পোস্ট করেন নি। এখানে একটি:

List<int> source = Enumerable.Range(1,23).ToList();
int batchsize = 10;
for (int i = 0; i < source.Count; i+= batchsize)
{
    var batch = source.Skip(i).Take(batchsize);
}

এই সরলতাটি সম্ভব কারণ টেক পদ্ধতি:

... sourceউপাদানগুলি গণনা করা হয় এবং ফলন countদেয় যতক্ষণ না উপাদানগুলি ফলন করা হয় বা sourceএতে আরও উপাদান থাকে না। যদি countউপাদানের সংখ্যা অতিক্রম করে source, সমস্ত উপাদানগুলি sourceফিরে আসে

দাবি অস্বীকার:

স্কিপ এন্ড টু লুপের অভ্যন্তরে ব্যবহার করার অর্থ হ'ল গণনা করা একাধিকবার গণিত হবে। এটি বিপজ্জনক যদি গণনা পিছনে দেওয়া হয়। এটি একটি ডাটাবেস ক্যোয়ারী, বা একটি ওয়েব অনুরোধ, বা একটি ফাইল পড়া একাধিক মৃত্যুদন্ড কার্যকর হতে পারে। এই উদাহরণটি স্পষ্টভাবে একটি তালিকার ব্যবহারের জন্য যা মুলতবিহীন নয়, সুতরাং এটি কোনও সমস্যা কম। এটি এখনও একটি ধীর সমাধান যেহেতু স্কিপ প্রতিটি বার যখনই ডাকা হয় তখন সংগ্রহকে গণনা করবে।

এটি GetRangeপদ্ধতিটি ব্যবহার করেও সমাধান করা যেতে পারে তবে সম্ভাব্য বিশ্রামের ব্যাচটি বের করার জন্য এটি অতিরিক্ত গণনা প্রয়োজন:

for (int i = 0; i < source.Count; i += batchsize)
{
    int remaining = source.Count - i;
    var batch = remaining > batchsize  ? source.GetRange(i, batchsize) : source.GetRange(i, remaining);
}

এটি পরিচালনা করার জন্য এখানে তৃতীয় উপায় রয়েছে যা 2 টি লুপের সাথে কাজ করে। এটি নিশ্চিত করে যে সংগ্রহটি কেবলমাত্র 1 বার গণিত হয়েছে !:

int batchsize = 10;
List<int> batch = new List<int>(batchsize);

for (int i = 0; i < source.Count; i += batchsize)
{
    // calculated the remaining items to avoid an OutOfRangeException
    batchsize = source.Count - i > batchsize ? batchsize : source.Count - i;
    for (int j = i; j < i + batchsize; j++)
    {
        batch.Add(source[j]);
    }           
    batch.Clear();
}

4
খুব সুন্দর সমাধান। লোকেরা কীভাবে লুপের জন্য ব্যবহার করবেন তা ভুলে গিয়েছিলেন
ভাইটালিকস

4
লুপটি ব্যবহার Skipএবং Takeঅভ্যন্তরের অর্থ হ'ল গণনা করা একাধিকবার গণিত হবে। এটি বিপজ্জনক যদি গণনা পিছনে দেওয়া হয়। এটি একটি ডাটাবেস ক্যোয়ারী, বা একটি ওয়েব অনুরোধ, বা একটি ফাইল পঠন একাধিক মৃত্যুদন্ড কার্যকর করতে পারে আপনার উদাহরণে আপনার এমন একটি রয়েছে Listযা স্থগিত নয়, সুতরাং এটি কোনও সমস্যা কম।
থিওডর জোলিয়াস

@ থিডোরজৌলিয়াস হ্যাঁ আমি জানি, এ কারণেই আমি আজ দ্বিতীয় সমাধান পোস্ট করেছি। আমি আপনার মন্তব্যটিকে অস্বীকৃতি হিসাবে পোস্ট করেছি, কারণ আপনি এটি বেশ ভালভাবে তৈরি করেছেন, আমি কি আপনাকে উদ্ধৃত করব?
মং ঝু

আমি 2 টি লুপের সাহায্যে তৃতীয় সমাধানটি লিখেছি যাতে সংগ্রহটি কেবল 1 বার গণনা করা হয়। স্কিপ.টেক জিনিসটি একটি অত্যন্ত অযোগ্য সমাধান
মং ঝু

4

মোরেলিংক হিসাবে একই পদ্ধতির, তবে অ্যারের পরিবর্তে তালিকা ব্যবহার করা। আমি বেঞ্চমার্কিং করিনি, তবে কিছু লোকের কাছে পঠনযোগ্যতা আরও গুরুত্বপূর্ণ:

    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
    {
        List<T> batch = new List<T>();

        foreach (var item in source)
        {
            batch.Add(item);

            if (batch.Count >= size)
            {
                yield return batch;
                batch.Clear();
            }
        }

        if (batch.Count > 0)
        {
            yield return batch;
        }
    }

4
আপনার ব্যাচ ভেরিয়েবল পুনরায় ব্যবহার করা উচিত নয়। আপনার গ্রাহকরা এটির মাধ্যমে সম্পূর্ণ বিভ্রান্ত হতে পারে। এছাড়াও, sizeপ্যারামিটারটির new Listআকারটি অনুকূল করতে আপনার পাস করুন ।
এরিক

4
সহজ ফিক্স: প্রতিস্থাপন batch.Clear();সঙ্গেbatch = new List<T>();
NetMage

4

এখানে নিক তিমির ( লিঙ্ক ) এবং ইনফোগুলচের ( লিঙ্ক ) অলস Batchবাস্তবায়নগুলির চেষ্টা করা উন্নতি রয়েছে । এই এক কঠোর। আপনি হয় সঠিক ক্রমে ব্যাচগুলি গণনা করুন, বা আপনি একটি ব্যতিক্রম পান।

public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
    this IEnumerable<TSource> source, int size)
{
    if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
    using (var enumerator = source.GetEnumerator())
    {
        int i = 0;
        while (enumerator.MoveNext())
        {
            if (i % size != 0) throw new InvalidOperationException(
                "The enumeration is out of order.");
            i++;
            yield return GetBatch();
        }
        IEnumerable<TSource> GetBatch()
        {
            while (true)
            {
                yield return enumerator.Current;
                if (i % size == 0 || !enumerator.MoveNext()) break;
                i++;
            }
        }
    }
}

এবং এখানে Batchটাইপ উত্স জন্য একটি অলস বাস্তবায়ন IList<T>। এটি গণনায় কোনও বিধিনিষেধ আরোপ করে না। ব্যাচগুলি কোনও ক্রমে এবং একাধিকবার আংশিকভাবে গণনা করা যায়। গণনার সময় সংগ্রহটি সংশোধন না করার সীমাবদ্ধতা যদিও এখনও রয়েছে। এটি enumerator.MoveNext()কোনও খণ্ড বা উপাদান সরবরাহের আগে একটি ডামি কল করে অর্জন করা হয় । ক্ষতিটি হ'ল গণনাটি নির্বিঘ্নে ছেড়ে দেওয়া হয়েছে, যেহেতু যখন গণনাটি শেষ হবে তখন এটি অজানা।

public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
    this IList<TSource> source, int size)
{
    if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
    var enumerator = source.GetEnumerator();
    for (int i = 0; i < source.Count; i += size)
    {
        enumerator.MoveNext();
        yield return GetChunk(i, Math.Min(i + size, source.Count));
    }
    IEnumerable<TSource> GetChunk(int from, int toExclusive)
    {
        for (int j = from; j < toExclusive; j++)
        {
            enumerator.MoveNext();
            yield return source[j];
        }
    }
}

2

সুতরাং একটি কার্যকরী টুপি চালু থাকলে এটি তুচ্ছ দেখা দেয় .... তবে সি # তে কিছু উল্লেখযোগ্য ডাউনসাইড রয়েছে।

আপনি সম্ভবত এটি আইএনউমারেবলের একটি উদ্ঘাটন হিসাবে দেখতে পেয়েছেন (গুগল এটি এবং আপনি সম্ভবত কিছু হাস্কেল ডক্সে শেষ করতে পারেন তবে কিছু এফ # স্টাফ উন্মুক্ত ব্যবহার করে থাকতে পারে, যদি আপনি জানেন F #, হাস্কেল ডক্সে স্কুইন্ট এবং এটি তৈরি করবে) ইন্দ্রিয়).

আনফোল্ডগুলি ভাঁজ ("সমষ্টিগত") সম্পর্কিত ইনপুটটি আইকনামারেবলের মাধ্যমে পুনরাবৃত্তি না করে এটি আউটপুট ডেটা স্ট্রাকচারের মাধ্যমে পুনরুক্তি করে (এটি আইইনুমারবেবল এবং আইওসবার্ভেবলের মধ্যে এর একই সম্পর্ক, বাস্তবে আমি মনে করি আইওবসার্ভেবল "জেনারেট" নামক একটি "উদ্ঘাটন" প্রয়োগ করে না)। ..)

যাইহোক প্রথমে আপনার একটি উদ্ঘাটন পদ্ধতিটি দরকার, আমি মনে করি এটি কাজ করে (দুর্ভাগ্যক্রমে এটি বড় "তালিকাগুলির" জন্য স্ট্যাকটি প্রস্ফুটিত করবে ... আপনি ফলন ব্যবহার করে এফ # তে নিরাপদে এটি লিখতে পারেন);

    static IEnumerable<T> Unfold<T, U>(Func<U, IEnumerable<Tuple<U, T>>> f, U seed)
    {
        var maybeNewSeedAndElement = f(seed);

        return maybeNewSeedAndElement.SelectMany(x => new[] { x.Item2 }.Concat(Unfold(f, x.Item1)));
    }

এটি কিছুটা অবসন্নতা কারণ সি # কার্যকরী ল্যাঙ্গুয়েজের কিছু গ্রহণযোগ্যতার জন্য বাস্তবায়িত করে না ... তবে এটি মূলত একটি বীজ নেয় এবং তারপরে আইনামিউরেবল এবং পরবর্তী বীজের পরবর্তী উপাদানটির "সম্ভবত" উত্তর উত্পন্ন করে (সম্ভবত সি # তে বিদ্যমান নেই, সুতরাং আমরা এটি জাল করার জন্য আইনিউমারবল ব্যবহার করেছি), এবং বাকী উত্তরের সাথে সম্মতি জানাই (আমি "ও (এন?)" এর জটিলতার জন্য কোনও প্রমাণ দিতে পারি না)।

একবার আপনি তা করতে পেরেছেন;

    static IEnumerable<IEnumerable<T>> Batch<T>(IEnumerable<T> xs, int n)
    {
        return Unfold(ys =>
            {
                var head = ys.Take(n);
                var tail = ys.Skip(n);
                return head.Take(1).Select(_ => Tuple.Create(tail, head));
            },
            xs);
    }

এটি দেখতে বেশ পরিষ্কার দেখাচ্ছে ... আপনি "এন" উপাদানগুলিকে আইইনুমারেবলের "পরবর্তী" উপাদান হিসাবে গ্রহণ করেন, এবং "লেজ "টি অপ্রকাশিত তালিকার বাকি অংশ।

যদি মাথার মধ্যে কিছু না থাকে ... আপনি শেষ হয়ে গেলেন ... আপনি "কিছুই না" ফিরিয়ে আনুন (তবে একটি ফাঁকা আইকনামেবল> হিসাবে নকল) ... অন্যথায় আপনি মাথা উপাদান এবং লেজ প্রক্রিয়াটি ফিরে পাবেন।

আপনি সম্ভবত IObservable ব্যবহার করে এটি করতে পারেন, সম্ভবত একটি "ব্যাচ" এর মতো পদ্ধতি ইতিমধ্যে রয়েছে এবং আপনি সম্ভবত এটি ব্যবহার করতে পারেন।

যদি স্ট্যাকের ঝুঁকিটি উদ্বিগ্ন হয়ে পড়ে (এটি সম্ভবত হওয়া উচিত) তবে আপনার এফ # তে প্রয়োগ করা উচিত (এবং সম্ভবত ইতিমধ্যে এটির সাথে কোনও F # লাইব্রেরি রয়েছে (FSharpX?))।

(আমি কেবল এর কয়েকটি প্রাথমিক পরীক্ষা করেছি, সুতরাং সেখানে বিজোড় বাগগুলি থাকতে পারে)।


Maybeসি # তে বিদ্যমান থাকতে পারে - উদাহরণস্বরূপ Optionভাষা এক্সট্রে দেখুন।
টিম ব্যারাস

1

আমি এটি খুব দেরিতে যোগদান করছি তবে আমি আরও আকর্ষণীয় কিছু পেয়েছি।

সুতরাং আমরা এখানে Skipএবং Takeআরও ভাল পারফরম্যান্সের জন্য ব্যবহার করতে পারি ।

public static class MyExtensions
    {
        public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items, int maxItems)
        {
            return items.Select((item, index) => new { item, index })
                        .GroupBy(x => x.index / maxItems)
                        .Select(g => g.Select(x => x.item));
        }

        public static IEnumerable<T> Batch2<T>(this IEnumerable<T> items, int skip, int take)
        {
            return items.Skip(skip).Take(take);
        }

    }

পরবর্তী আমি 100000 রেকর্ডের সাথে চেক করেছি। লুপিং এর ক্ষেত্রে আরও সময় নিচ্ছেBatch

কনসোল অ্যাপ্লিকেশন কোড।

static void Main(string[] args)
{
    List<string> Ids = GetData("First");
    List<string> Ids2 = GetData("tsriF");

    Stopwatch FirstWatch = new Stopwatch();
    FirstWatch.Start();
    foreach (var batch in Ids2.Batch(5000))
    {
        // Console.WriteLine("Batch Ouput:= " + string.Join(",", batch));
    }
    FirstWatch.Stop();
    Console.WriteLine("Done Processing time taken:= "+ FirstWatch.Elapsed.ToString());


    Stopwatch Second = new Stopwatch();

    Second.Start();
    int Length = Ids2.Count;
    int StartIndex = 0;
    int BatchSize = 5000;
    while (Length > 0)
    {
        var SecBatch = Ids2.Batch2(StartIndex, BatchSize);
        // Console.WriteLine("Second Batch Ouput:= " + string.Join(",", SecBatch));
        Length = Length - BatchSize;
        StartIndex += BatchSize;
    }

    Second.Stop();
    Console.WriteLine("Done Processing time taken Second:= " + Second.Elapsed.ToString());
    Console.ReadKey();
}

static List<string> GetData(string name)
{
    List<string> Data = new List<string>();
    for (int i = 0; i < 100000; i++)
    {
        Data.Add(string.Format("{0} {1}", name, i.ToString()));
    }

    return Data;
}

সময় নেওয়া এই রকম।

প্রথম - 00: 00: 00.0708, 00: 00: 00.0660

দ্বিতীয় (নিন এবং এক ছেড়ে যান) - 00: 00: 00.0008, 00: 00: 00.0008


4
GroupByএটি একটি একক সারি উত্পাদন করার আগে সম্পূর্ণরূপে গণনা করে। এটি ব্যাচিং করার ভাল উপায় নয়।
এরিক

@ এরিক এটি আপনার অর্জনের চেষ্টা করছেন তার উপর নির্ভর করে। যদি ব্যাচিংটি সমস্যা না হয় এবং প্রসেসিংয়ের জন্য আপনার কেবল আইটেমগুলিকে ছোট ছোট ভাগে ভাগ করতে হবে এটি কেবল জিনিস হতে পারে। আমি এটি এমএসসিআরএম এর জন্য ব্যবহার করছি যেখানে ১০০ টি রেকর্ড থাকতে পারে যা ল্যাম্বডিএ'র ব্যাচে কোনও সমস্যা নেই .. এটির সেকেন্ড সময় লাগে ..
জেনসবি

4
অবশ্যই, এমন ব্যবহারের কেস রয়েছে যেখানে সম্পূর্ণ গণনা বিবেচনা করে না। আপনি যখন একটি চমত্কার লিখতে পারেন তবে কেন দ্বিতীয় শ্রেণির ইউটিলিটি পদ্ধতি লিখবেন?
এরিক

ভাল বিকল্প তবে অভিন্ন নয় যেমন প্রথমে তালিকার একটি তালিকা দেয় যা আপনাকে লুপ করতে দেয়।
গ্যারেথ হপকিন্স

পরিবর্তন foreach (var batch in Ids2.Batch(5000))করুন var gourpBatch = Ids2.Batch(5000)এবং সময়োচিত ফলাফল চেক করুন। বা সময়টিতে var SecBatch = Ids2.Batch2(StartIndex, BatchSize);পরিবর্তনের জন্য যদি আপনার ফলাফলগুলি আগ্রহী তবে আমি টোলিস্ট যুক্ত করব ।
Seabizkit

1

আমি একটি কাস্টম আইনিংরেবল বাস্তবায়ন লিখেছি যা লিনাক ছাড়াই কাজ করে এবং ডেটাগুলির উপরে একক অঙ্কের নিশ্চয়তা দেয়। এটি ব্যাকিং তালিকাগুলি বা অ্যারেগুলির প্রয়োজন ছাড়াই এই সমস্ত কাজ সম্পাদন করে যা বড় ডেটা সেটগুলিতে মেমরি বিস্ফোরণ ঘটায়।

এখানে কিছু প্রাথমিক পরীক্ষা দেওয়া হল:

    [Fact]
    public void ShouldPartition()
    {
        var ints = new List<int> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
        var data = ints.PartitionByMaxGroupSize(3);
        data.Count().Should().Be(4);

        data.Skip(0).First().Count().Should().Be(3);
        data.Skip(0).First().ToList()[0].Should().Be(0);
        data.Skip(0).First().ToList()[1].Should().Be(1);
        data.Skip(0).First().ToList()[2].Should().Be(2);

        data.Skip(1).First().Count().Should().Be(3);
        data.Skip(1).First().ToList()[0].Should().Be(3);
        data.Skip(1).First().ToList()[1].Should().Be(4);
        data.Skip(1).First().ToList()[2].Should().Be(5);

        data.Skip(2).First().Count().Should().Be(3);
        data.Skip(2).First().ToList()[0].Should().Be(6);
        data.Skip(2).First().ToList()[1].Should().Be(7);
        data.Skip(2).First().ToList()[2].Should().Be(8);

        data.Skip(3).First().Count().Should().Be(1);
        data.Skip(3).First().ToList()[0].Should().Be(9);
    }

ডেটা পার্টিশনের এক্সটেনশন পদ্ধতি।

/// <summary>
/// A set of extension methods for <see cref="IEnumerable{T}"/>. 
/// </summary>
public static class EnumerableExtender
{
    /// <summary>
    /// Splits an enumerable into chucks, by a maximum group size.
    /// </summary>
    /// <param name="source">The source to split</param>
    /// <param name="maxSize">The maximum number of items per group.</param>
    /// <typeparam name="T">The type of item to split</typeparam>
    /// <returns>A list of lists of the original items.</returns>
    public static IEnumerable<IEnumerable<T>> PartitionByMaxGroupSize<T>(this IEnumerable<T> source, int maxSize)
    {
        return new SplittingEnumerable<T>(source, maxSize);
    }
}

এটিই বাস্তবায়নকারী শ্রেণি

    using System.Collections;
    using System.Collections.Generic;

    internal class SplittingEnumerable<T> : IEnumerable<IEnumerable<T>>
    {
        private readonly IEnumerable<T> backing;
        private readonly int maxSize;
        private bool hasCurrent;
        private T lastItem;

        public SplittingEnumerable(IEnumerable<T> backing, int maxSize)
        {
            this.backing = backing;
            this.maxSize = maxSize;
        }

        public IEnumerator<IEnumerable<T>> GetEnumerator()
        {
            return new Enumerator(this, this.backing.GetEnumerator());
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        private class Enumerator : IEnumerator<IEnumerable<T>>
        {
            private readonly SplittingEnumerable<T> parent;
            private readonly IEnumerator<T> backingEnumerator;
            private NextEnumerable current;

            public Enumerator(SplittingEnumerable<T> parent, IEnumerator<T> backingEnumerator)
            {
                this.parent = parent;
                this.backingEnumerator = backingEnumerator;
                this.parent.hasCurrent = this.backingEnumerator.MoveNext();
                if (this.parent.hasCurrent)
                {
                    this.parent.lastItem = this.backingEnumerator.Current;
                }
            }

            public bool MoveNext()
            {
                if (this.current == null)
                {
                    this.current = new NextEnumerable(this.parent, this.backingEnumerator);
                    return true;
                }
                else
                {
                    if (!this.current.IsComplete)
                    {
                        using (var enumerator = this.current.GetEnumerator())
                        {
                            while (enumerator.MoveNext())
                            {
                            }
                        }
                    }
                }

                if (!this.parent.hasCurrent)
                {
                    return false;
                }

                this.current = new NextEnumerable(this.parent, this.backingEnumerator);
                return true;
            }

            public void Reset()
            {
                throw new System.NotImplementedException();
            }

            public IEnumerable<T> Current
            {
                get { return this.current; }
            }

            object IEnumerator.Current
            {
                get { return this.Current; }
            }

            public void Dispose()
            {
            }
        }

        private class NextEnumerable : IEnumerable<T>
        {
            private readonly SplittingEnumerable<T> splitter;
            private readonly IEnumerator<T> backingEnumerator;
            private int currentSize;

            public NextEnumerable(SplittingEnumerable<T> splitter, IEnumerator<T> backingEnumerator)
            {
                this.splitter = splitter;
                this.backingEnumerator = backingEnumerator;
            }

            public bool IsComplete { get; private set; }

            public IEnumerator<T> GetEnumerator()
            {
                return new NextEnumerator(this.splitter, this, this.backingEnumerator);
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }

            private class NextEnumerator : IEnumerator<T>
            {
                private readonly SplittingEnumerable<T> splitter;
                private readonly NextEnumerable parent;
                private readonly IEnumerator<T> enumerator;
                private T currentItem;

                public NextEnumerator(SplittingEnumerable<T> splitter, NextEnumerable parent, IEnumerator<T> enumerator)
                {
                    this.splitter = splitter;
                    this.parent = parent;
                    this.enumerator = enumerator;
                }

                public bool MoveNext()
                {
                    this.parent.currentSize += 1;
                    this.currentItem = this.splitter.lastItem;
                    var hasCcurent = this.splitter.hasCurrent;

                    this.parent.IsComplete = this.parent.currentSize > this.splitter.maxSize;

                    if (this.parent.IsComplete)
                    {
                        return false;
                    }

                    if (hasCcurent)
                    {
                        var result = this.enumerator.MoveNext();

                        this.splitter.lastItem = this.enumerator.Current;
                        this.splitter.hasCurrent = result;
                    }

                    return hasCcurent;
                }

                public void Reset()
                {
                    throw new System.NotImplementedException();
                }

                public T Current
                {
                    get { return this.currentItem; }
                }

                object IEnumerator.Current
                {
                    get { return this.Current; }
                }

                public void Dispose()
                {
                }
            }
        }
    }

1

আরও একটি লাইন বাস্তবায়ন। এটি খালি তালিকার সাথেও কাজ করে, এক্ষেত্রে আপনি একটি শূন্য আকারের ব্যাচের সংগ্রহ পাবেন।

var aList = Enumerable.Range(1, 100).ToList(); //a given list
var size = 9; //the wanted batch size
//number of batches are: (aList.Count() + size - 1) / size;

var batches = Enumerable.Range(0, (aList.Count() + size - 1) / size).Select(i => aList.GetRange( i * size, Math.Min(size, aList.Count() - i * size)));

Assert.True(batches.Count() == 12);
Assert.AreEqual(batches.ToList().ElementAt(0), new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
Assert.AreEqual(batches.ToList().ElementAt(1), new List<int>() { 10, 11, 12, 13, 14, 15, 16, 17, 18 });
Assert.AreEqual(batches.ToList().ElementAt(11), new List<int>() { 100 });

1

আর একটি উপায় হ'ল আরএক্স বাফার অপারেটর ব্যবহার করা

//using System.Linq;
//using System.Reactive.Linq;
//using System.Reactive.Threading.Tasks;

var observableBatches = anAnumerable.ToObservable().Buffer(size);

var batches = aList.ToObservable().Buffer(size).ToList().ToTask().GetAwaiter().GetResult();

4
আপনার কখনই ব্যবহার করা উচিত নয় GetAwaiter().GetResult()। এটি সিঙ্ক্রোনাস কোডের জন্য জোরপূর্বক অ্যাসিঙ্ক কোডটি কল করার জন্য একটি কোড গন্ধ।
gfache

1

ব্যবহার এবং বুঝতে একটি সহজ সংস্করণ।

    public static List<List<T>> chunkList<T>(List<T> listToChunk, int batchSize)
    {
        List<List<T>> batches = new List<List<T>>();

        if (listToChunk.Count == 0) return batches;

        bool moreRecords = true;
        int fromRecord = 0;
        int countRange = 0;
        if (listToChunk.Count >= batchSize)
        {
            countRange = batchSize;
        }
        else
        {
            countRange = listToChunk.Count;
        }

        while (moreRecords)
        {
            List<T> batch = listToChunk.GetRange(fromRecord, countRange);
            batches.Add(batch);

            if ((fromRecord + batchSize) >= listToChunk.Count)
            {
                moreRecords = false;
            }

            fromRecord = fromRecord + batch.Count;

            if ((fromRecord + batchSize) > listToChunk.Count)
            {
                countRange = listToChunk.Count - fromRecord;
            }
            else
            {
                countRange = batchSize;
            }
        }
        return batches;
    }

0

আমি জানি সবাই এই কাজটি করার জন্য জটিল সিস্টেম ব্যবহার করেছিল এবং কেন এটি সত্যই তা পাই না। টেক এন্ড স্কিপ Func<TSource,Int32,TResult>ট্রান্সফর্ম ফাংশন সহ সাধারণ নির্বাচনগুলি ব্যবহার করে সেই সমস্ত ক্রিয়াকলাপের অনুমতি দেবে । পছন্দ:

public IEnumerable<IEnumerable<T>> Buffer<T>(IEnumerable<T> source, int size)=>
    source.Select((item, index) => source.Skip(size * index).Take(size)).TakeWhile(bucket => bucket.Any());

4
এটি খুব অদক্ষ হতে পারে, কারণ sourceপ্রদত্তটি প্রায়শই পুনরাবৃত্তি হবে।
কেভিন মেয়ার

4
এটি কেবল অদক্ষ নয়, তবে ভুল ফলাফলও তৈরি করতে পারে। দু'বার গণনা করা হলে একটি গণনাকারী একই উপাদানগুলি সরবরাহ করবে বলে কোনও গ্যারান্টি নেই। একটি উদাহরণ হিসাবে এই গণনীয় নিন: Enumerable.Range(0, 1).SelectMany(_ => Enumerable.Range(0, new Random().Next()))
থিওডর জৌলিয়াস

-3
    static IEnumerable<IEnumerable<T>> TakeBatch<T>(IEnumerable<T> ts,int batchSize)
    {
        return from @group in ts.Select((x, i) => new { x, i }).ToLookup(xi => xi.i / batchSize)
               select @group.Select(xi => xi.x);
    }

আপনার উত্তরে কিছু বিবরণ / পাঠ্য যুক্ত করুন। শুধুমাত্র কোড দেওয়া বেশিরভাগ সময় কম অর্থ হতে পারে।
আরিফুল হক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.