কেউ কি লিন্কে একটি নির্দিষ্ট আকারের ব্যাচ তৈরির কোনও উপায় প্রস্তাব করতে পারেন?
আদর্শভাবে আমি কিছু কনফিগারযোগ্য পরিমাণের অংশগুলিতে অপারেশন করতে সক্ষম হতে চাই।
উত্তর:
আপনার কোনও কোড লেখার দরকার নেই। মোরলিংকিউ ব্যাচ পদ্ধতিটি ব্যবহার করুন , যা উত্স ক্রমটিকে আকারের বালতিতে ব্যাচ করে (আপনি ইনস্টল করতে পারেন এমন নুগেট প্যাকেজ হিসাবে মোরলিংকিউ উপলব্ধ):
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();
}
Batch(new int[] { 1, 2 }, 1000000)
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
GroupBy
গণনা শুরু হলে , এটির উত্সটি পুরোপুরি গণনা করতে হবে না? এটি উত্সটির অলস মূল্যায়ন হারায় এবং এইভাবে, কিছু ক্ষেত্রে, ব্যাচিংয়ের সমস্ত সুবিধা!
যদি আপনি 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
}
উপরের সমস্তগুলি বড় ব্যাচ বা লো মেমরি স্পেসের সাথে ভয়ঙ্করভাবে সঞ্চালন করে। পাইপলাইনের জন্য আমার নিজের লিখতে হবে (কোথাও কোনও আইটেম জমে না সেদিকে লক্ষ্য করুন):
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())
এটি সম্পূর্ণ অলস, লো ওভারহেড, ব্যাচের এক-ফাংশন বাস্তবায়ন যা কোনও জমে না। এরিকরোলারের সাহায্যে নিক তিমির সমাধান (এবং এতে সমস্যার সমাধান করে) এর ভিত্তিতে।
ইন্টেরেশন সরাসরি অন্তর্নিহিত আইমনামেবল থেকে আসে, সুতরাং উপাদানগুলি অবশ্যই কঠোর ক্রমে গণনা করতে হবে এবং একাধিকবার অ্যাক্সেস করা উচিত নয়। যদি কিছু উপাদান কোনও অভ্যন্তরীণ লুপে গ্রাস না করা হয় তবে সেগুলি ফেলে দেওয়া হয় (এবং সেভ করা পুনরুত্থকটির মাধ্যমে আবার এগুলিতে অ্যাক্সেসের চেষ্টা করা নিক্ষেপ করবে 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
}
}
}
done
সর্বদা কল করার e.Count()
পরে চেকটি অপসারণ করতে পারেন yield return e
। আপনি অনির্ধারিত আচরণ ডাকা না BatchInner লুপ পুনরায় সাজাতে হবে source.Current
যদি i >= size
। এটি BatchInner
প্রতিটি ব্যাচের জন্য নতুন বরাদ্দের প্রয়োজনীয়তা দূর করবে ।
i
তাই এটি পৃথক শ্রেণির সংজ্ঞা নির্ধারণের চেয়ে অগত্যা আরও কার্যকর নয়, তবে এটি আমার মনে হয় যে এটি একটু পরিচ্ছন্ন।
আমি অবাক হয়েছি কেন কেন কেউ লুপ সমাধানের জন্য কোনও পুরানো স্কুল পোস্ট করেন নি। এখানে একটি:
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();
}
Skip
এবং Take
অভ্যন্তরের অর্থ হ'ল গণনা করা একাধিকবার গণিত হবে। এটি বিপজ্জনক যদি গণনা পিছনে দেওয়া হয়। এটি একটি ডাটাবেস ক্যোয়ারী, বা একটি ওয়েব অনুরোধ, বা একটি ফাইল পঠন একাধিক মৃত্যুদন্ড কার্যকর করতে পারে আপনার উদাহরণে আপনার এমন একটি রয়েছে List
যা স্থগিত নয়, সুতরাং এটি কোনও সমস্যা কম।
মোরেলিংক হিসাবে একই পদ্ধতির, তবে অ্যারের পরিবর্তে তালিকা ব্যবহার করা। আমি বেঞ্চমার্কিং করিনি, তবে কিছু লোকের কাছে পঠনযোগ্যতা আরও গুরুত্বপূর্ণ:
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;
}
}
size
প্যারামিটারটির new List
আকারটি অনুকূল করতে আপনার পাস করুন ।
batch.Clear();
সঙ্গেbatch = new List<T>();
এখানে নিক তিমির ( লিঙ্ক ) এবং ইনফোগুলচের ( লিঙ্ক ) অলস 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];
}
}
}
সুতরাং একটি কার্যকরী টুপি চালু থাকলে এটি তুচ্ছ দেখা দেয় .... তবে সি # তে কিছু উল্লেখযোগ্য ডাউনসাইড রয়েছে।
আপনি সম্ভবত এটি আইএনউমারেবলের একটি উদ্ঘাটন হিসাবে দেখতে পেয়েছেন (গুগল এটি এবং আপনি সম্ভবত কিছু হাস্কেল ডক্সে শেষ করতে পারেন তবে কিছু এফ # স্টাফ উন্মুক্ত ব্যবহার করে থাকতে পারে, যদি আপনি জানেন 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
ভাষা এক্সট্রে দেখুন।
আমি এটি খুব দেরিতে যোগদান করছি তবে আমি আরও আকর্ষণীয় কিছু পেয়েছি।
সুতরাং আমরা এখানে 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
GroupBy
এটি একটি একক সারি উত্পাদন করার আগে সম্পূর্ণরূপে গণনা করে। এটি ব্যাচিং করার ভাল উপায় নয়।
foreach (var batch in Ids2.Batch(5000))
করুন var gourpBatch = Ids2.Batch(5000)
এবং সময়োচিত ফলাফল চেক করুন। বা সময়টিতে var SecBatch = Ids2.Batch2(StartIndex, BatchSize);
পরিবর্তনের জন্য যদি আপনার ফলাফলগুলি আগ্রহী তবে আমি টোলিস্ট যুক্ত করব ।
আমি একটি কাস্টম আইনিংরেবল বাস্তবায়ন লিখেছি যা লিনাক ছাড়াই কাজ করে এবং ডেটাগুলির উপরে একক অঙ্কের নিশ্চয়তা দেয়। এটি ব্যাকিং তালিকাগুলি বা অ্যারেগুলির প্রয়োজন ছাড়াই এই সমস্ত কাজ সম্পাদন করে যা বড় ডেটা সেটগুলিতে মেমরি বিস্ফোরণ ঘটায়।
এখানে কিছু প্রাথমিক পরীক্ষা দেওয়া হল:
[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()
{
}
}
}
}
আরও একটি লাইন বাস্তবায়ন। এটি খালি তালিকার সাথেও কাজ করে, এক্ষেত্রে আপনি একটি শূন্য আকারের ব্যাচের সংগ্রহ পাবেন।
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 });
আর একটি উপায় হ'ল আরএক্স বাফার অপারেটর ব্যবহার করা
//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();
GetAwaiter().GetResult()
। এটি সিঙ্ক্রোনাস কোডের জন্য জোরপূর্বক অ্যাসিঙ্ক কোডটি কল করার জন্য একটি কোড গন্ধ।
ব্যবহার এবং বুঝতে একটি সহজ সংস্করণ।
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;
}
আমি জানি সবাই এই কাজটি করার জন্য জটিল সিস্টেম ব্যবহার করেছিল এবং কেন এটি সত্যই তা পাই না। টেক এন্ড স্কিপ 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());
source
প্রদত্তটি প্রায়শই পুনরাবৃত্তি হবে।
Enumerable.Range(0, 1).SelectMany(_ => Enumerable.Range(0, new Random().Next()))
।
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);
}