From 4c583f22128fad373a13568ac8803461f27c98ec Mon Sep 17 00:00:00 2001 From: Orace Date: Sat, 14 Jan 2023 18:18:41 +0100 Subject: [PATCH] Refactor "Interleave" to use linked list This is a squashed merge of PR #726. Co-authored-by: Atif Aziz --- MoreLinq/Interleave.cs | 53 +++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/MoreLinq/Interleave.cs b/MoreLinq/Interleave.cs index a85c20977..47ec2af7e 100644 --- a/MoreLinq/Interleave.cs +++ b/MoreLinq/Interleave.cs @@ -50,54 +50,55 @@ public static IEnumerable Interleave(this IEnumerable sequence, params if (otherSequences.Any(s => s == null)) throw new ArgumentNullException(nameof(otherSequences), "One or more sequences passed to Interleave was null."); - return _(); IEnumerable _() + return _(otherSequences.Prepend(sequence)); + + static IEnumerable _(IEnumerable> sequences) { - var sequences = new[] { sequence }.Concat(otherSequences); - var enumerators = new List?>(); + var enumerators = new LinkedList>(); try { - foreach (var enumerator in sequences.Select(s => s.GetEnumerator())) + // First, yield first element of each sequence. + + foreach (var sequence in sequences) { - enumerators.Add(enumerator); + var enumerator = sequence.GetEnumerator(); + + enumerators.AddLast(enumerator); if (enumerator.MoveNext()) { yield return enumerator.Current; } - else + else // Dispose and remove empty sequence { - enumerators.Remove(enumerator); enumerator.Dispose(); + enumerators.Remove(enumerator); } } - var hasNext = true; - while (hasNext) + // Then, yield remaining elements from each sequence. + + var node = enumerators.First; + while (node is { Value: var enumerator, Next: var nextNode }) { - hasNext = false; - for (var i = 0; i < enumerators.Count; i++) + if (enumerator.MoveNext()) { - var enumerator = enumerators[i]; - if (enumerator == null) - continue; - - if (enumerator.MoveNext()) - { - hasNext = true; - yield return enumerator.Current; - } - else - { - enumerators[i] = null; - enumerator.Dispose(); - } + yield return enumerator.Current; } + else + { + enumerator.Dispose(); + enumerators.Remove(node); + } + + // Work on next node or restart from first one. + node = nextNode ?? enumerators.First; } } finally { foreach (var enumerator in enumerators) - enumerator?.Dispose(); + enumerator.Dispose(); } } }