我嘗試實作自定義 Linq Chunk 函式并找到了這個代碼示例
This function should separate IEnumerable into IEnumerable of concrete size
public static class EnumerableExtentions
{
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
int i = 0;
IEnumerable<T> Batch()
{
do yield return enumerator.Current;
while ( i < size && enumerator.MoveNext());
}
yield return Batch();
}
}
}
}
所以,我有一個問題。為什么當我嘗試對結果執行一些 Linq 操作時,它們是不正確的?例如:
IEnumerable<int> list = Enumerable.Range(0, 10);
Console.WriteLine(list.Batch(2).Count()); // 10 instead of 5
我有一個假設,它的發生是因為內部 IEnumerable Batch() 僅在呼叫 Count() 時觸發,并且那里出了問題,但我不知道到底是什么。
uj5u.com熱心網友回復:
您在迭代器中創建了一個迭代器,但只有外部迭代器在Count(). 如果你想執行你需要列舉它的內部,例如:
var batches = list.Batch(3);
foreach(var batch in batches) // the outer is executed
{
int count = batch.Count(); // the inner iterator is executed now
}
好吧,我會為這樣的方法建議一種不同的Chunk方法:
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
{
T[]? bucket = null;
var count = 0;
foreach (var item in source)
{
bucket ??= new T[size];
bucket[count ] = item;
if (count != size)
continue;
yield return bucket;
bucket = null;
count = 0;
}
if (count > 0)
{
Array.Resize(ref bucket, count);
yield return bucket;
}
}
uj5u.com熱心網友回復:
我有一個假設,它發生是因為內部 IEnumerable Batch() 僅在呼叫 Count() 時觸發
恰恰相反。當您呼叫時,內部不會IEnumerable被消耗。只消耗外層,也就是這個:CountCountIEnumerable
while (enumerator.MoveNext())
{
int i = 0;
IEnumerable<T> Batch()
{
// the below is not executed by Count!
// do yield return enumerator.Current;
// while ( i < size && enumerator.MoveNext());
}
yield return Batch();
}
所以Count要做的就是將列舉數移動到末尾,并計算它移動了多少次,即 10。
將其與本文的作者可能打算如何使用它進行比較:
foreach (var batch in someEnumerable.Batch(2)) {
foreach(var thing in batch) {
// ...
}
}
我還IEnumerable使用內部回圈使用內部 s,因此在內部運行代碼Batch。這將產生當前元素,然后也將源列舉器向前移動。 i < size它在檢查失敗之前再次產生當前元素。外部回圈將再次向前移動列舉器以進行下一次迭代。這就是您創建包含兩個元素的“批次”的方式。
請注意,上一段中的“列舉器”(來自someEnumerable)在內部和外部IEnumerables 之間共享。消耗內部或外部IEnumerable都會移動列舉器,只有當您以非常特定的方式同時消耗內部和外部IEnumerables 時,上一段中的事情順序才會發生,從而導致您獲得批次。
IEnumerable在您的情況下,您可以通過呼叫來使用內部s ToList:
Console.WriteLine(list.Batch(2).Select(x => x.ToList()).Count()); // 5
雖然在此處共享列舉器允許延遲使用批次,但它限制客戶端代碼僅以非常特定的方式使用它。在 .NET 6 實作中Chunk,批次(塊)被急切地計算為陣列:
public static IEnumerable<TSource[]> Chunk<TSource>(this IEnumerable<TSource> source, int size)
你可以Batch通過呼叫ToArray()這里做類似的事情:
yield return Batch().ToArray();
這樣內部IEnumerable的 s 總是被消耗掉。
uj5u.com熱心網友回復:
試試這種方式:
public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> arr, int size)
{
for (var i = 0; i < arr.Count() / size 1; i )
{
yield return arr.Skip(i * size).Take(size);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/523786.html
上一篇:如何為IEnumerable賦值
