我想采用流式 IEnumerable 值,例如:(出于說明目的顯示的元組。實際應用程式將從 DataReader 流式傳輸 DataRecords)
var tuples = new(int, int)[]
{
(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (2, 0), (2, 1), (2, 2),
};
我想保持狀態并注意左側欄位中的每一個變化。每組具有共同左側欄位的專案都應作為一系列 IEnumerables 回傳(資料將被預先排序,因此我不必擔心在本地端進行整理)。
(0,0) (0,1) (0,2) (0,3)
(1,0) (1,1)
(2,0) (2,1) (2,2)
這可能是不可能的,但希望在不創建任何將記錄存盤在 RAM 中的每組臨時串列的情況下做到這一點,因為每個組都相當大。換句話說,以某種方式讓每個組以某種方式神奇地從原始的 IEnumerable 中抽出,使其完全只向前,一次通過。
內部的 TakeWhile似乎可能是要走的路,但它總是tg從零開始重新開始迭代。
private int currentGroup;
public IEnumerator<IEnumerable<Tuple<int, int>>> GetEnumerator()
{
var tg = TupleGenerator();
foreach (Tuple<int, int> item in tg)
{
currentGroup = item.Item1;
yield return tg.TakeWhile((x) => x.Item1 == currentGroup);
}
}
static IEnumerable<Tuple<int, int>> TupleGenerator()
{
for (int i = 0; i < 10; i )
{
for (int j = 0; j < 10; j )
{
yield return new Tuple<int, int>(i,j);
}
}
}
uj5u.com熱心網友回復:
因此,雖然可以避免將整個組的資料存盤在記憶體中,并且只在您請求時使用恒定數量的記憶體來流式傳輸每個專案,但也有缺點。重要的是要認識到為什么緩沖是此類問題的典型解決方案。首先,避免緩沖的代碼更加復雜。其次,消費者IEnumerable在請求下一個組之前總是迭代每個內部以完成,并且沒有內部IEnumerable永遠開始被迭代不止一次。如果你違反了這些規則中的任何一條(如果你沒有明確地檢查它們),那么事情就會默默地出錯。(您最終會得到應該在多個組中的同一組中的資料,組數錯誤等)鑒于這些錯誤的容易程度以及搞砸的后果,確實值得明確檢查為他們拋出例外,因此消費者至少知道這是錯誤的并且需要修復。
至于實際代碼,您確實需要手動執行很多操作,以直接控制列舉器以及何時獲得專案,但其核心代碼主要歸結為跟蹤前一個專案并繼續產生只要當前專案與其“匹配”,專案就屬于同一組。
public static IEnumerable<IEnumerable<T>> GroupWhile<T>(
this IEnumerable<T> source,
Func<T, T, bool> predicate)
{
using (var iterator = source.GetEnumerator())
{
bool previousGroupFinished = true;
bool sourceExhaused = !iterator.MoveNext();
while (!sourceExhaused)
{
if (!previousGroupFinished)
throw new InvalidOperationException("It is not valid to request the next group until the previous group has run to completion");
previousGroupFinished = false;
bool startedIteratingCurrentGroup = false;
yield return NextGroup();
IEnumerable<T> NextGroup()
{
if (startedIteratingCurrentGroup)
throw new InvalidOperationException("This sequence doesn't support being iterated multiple times.");
startedIteratingCurrentGroup = true;
T previous;
do
{
yield return iterator.Current;
previous = iterator.Current;
sourceExhaused = !iterator.MoveNext();
}
while (!sourceExhaused && predicate(previous, iterator.Current));
previousGroupFinished = true;
}
}
}
}
要在您的情況下使用它,它非常簡單,您的專案被分組,而對中的第一個專案是相等的,但是您可以使用您想要的任何任意條件進行分組。
var data = new[] { (0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (2, 0), (2, 1), (2, 2) };
var grouped = data.GroupWhile((previous, current) => previous.Item1 == current.Item1);
foreach (var group in grouped)
{
Console.WriteLine(String.Join(", ", group));
}
與根據謂詞進行分組不同,根據某個關鍵物件進行分組通常也很方便。在您的示例中,鍵只是元組中的第一項。但是,如果計算鍵很昂貴或者不能多次計算,您可以更改分組機制以使用鍵選擇器而不是謂詞,并存盤前一個鍵而不是前一個專案。它產生的代碼非常相似,但略有不同:
public static IEnumerable<IEnumerable<TSource>> GroupAdjacent<TSource, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey> keyComparer = null)
{
keyComparer = keyComparer ?? EqualityComparer<TKey>.Default;
using (var iterator = source.GetEnumerator())
{
bool previousGroupFinished = true;
bool sourceExhaused = !iterator.MoveNext();
TKey nextKey = keySelector(iterator.Current);
while (!sourceExhaused)
{
if (!previousGroupFinished)
throw new InvalidOperationException("It is not valid to request the next group until the previous group has run to completion");
previousGroupFinished = false;
bool startedIteratingCurrentGroup = false;
yield return NextGroup();
IEnumerable<TSource> NextGroup()
{
if (startedIteratingCurrentGroup)
throw new InvalidOperationException("This sequence doesn't support being iterated multiple times.");
startedIteratingCurrentGroup = true;
TKey previousKey;
do
{
yield return iterator.Current;
sourceExhaused = !iterator.MoveNext();
previousKey = nextKey;
if (!sourceExhaused)
nextKey = keySelector(iterator.Current);
}
while (!sourceExhaused && keyComparer.Equals(previousKey, nextKey));
previousGroupFinished = true;
}
}
}
}
允許你寫:
var data = new[] { (0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (2, 0), (2, 1), (2, 2) };
var grouped = data.GroupAdjacent(pair => pair.Item1);
foreach (var group in grouped)
{
Console.WriteLine(String.Join(", ", group));
}
uj5u.com熱心網友回復:
使用MoreLinq 的GroupAdjacent方法:
根據指定的鍵選擇器函式對序列的相鄰元素進行分組。
var groupings = GetValues().GroupAdjacent(tuple => tuple.Item1);
foreach (var grouping in groupings)
{
Console.WriteLine($"Value: {grouping.Key}. Elements: {string.Join(", ", grouping)}");
}
IEnumerable<(int, int)> GetValues()
{
yield return (0, 0);
yield return (0, 1);
yield return (0, 2);
yield return (0, 3);
yield return (1, 0);
yield return (1, 1);
yield return (2, 0);
yield return (2, 1);
yield return (2, 2);
};
這將輸出以下內容:
值:0。元素:(0, 0), (0, 1), (0, 2), (0, 3)
值:1。元素:(1, 0), (1, 1)
值:2。元素:(2, 0), (2, 1), (2, 2)
可列舉上的每個實體groupings都是一個IGrouping<int, (int, int)>在列舉時會產生您需要的結果。
(PS:迭代器在這個實作中只被命中一次,所以它完全是只向前的)
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/532867.html
標籤:C#林克迭代不可数分子
