Skip to content

Commit

Permalink
Use static iterator methods to avoid closure
Browse files Browse the repository at this point in the history
This is a squashed merge of PR #1064 that:

- closes #906
- fixes #1065

---------

Co-authored-by: Atif Aziz <[email protected]>
  • Loading branch information
JacobSilasBentley and atifaziz authored May 1, 2024
1 parent 9e8073d commit 017e477
Show file tree
Hide file tree
Showing 51 changed files with 326 additions and 89 deletions.
2 changes: 1 addition & 1 deletion MoreLinq.Test/RankTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public void TestRankGroupedItems()
.Concat(Enumerable.Range(0, count))
.Concat(Enumerable.Range(0, count));
using var ts = sequence.AsTestingSequence();
var result = ts.Rank();
var result = ts.Rank().ToArray();

Assert.That(result.Distinct().Count(), Is.EqualTo(count));
Assert.That(result, Is.EqualTo(sequence.Reverse().Select(x => x + 1)));
Expand Down
4 changes: 3 additions & 1 deletion MoreLinq/Assert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ public static IEnumerable<TSource> Assert<TSource>(this IEnumerable<TSource> sou
if (source == null) throw new ArgumentNullException(nameof(source));
if (predicate == null) throw new ArgumentNullException(nameof(predicate));

return _(); IEnumerable<TSource> _()
return _(source, predicate, errorSelector);

static IEnumerable<TSource> _(IEnumerable<TSource> source, Func<TSource, bool> predicate, Func<TSource, Exception>? errorSelector)
{
foreach (var element in source)
{
Expand Down
4 changes: 3 additions & 1 deletion MoreLinq/AssertCount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ public static IEnumerable<TSource> AssertCount<TSource>(this IEnumerable<TSource
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
if (errorSelector == null) throw new ArgumentNullException(nameof(errorSelector));

return _(); IEnumerable<TSource> _()
return _(source, count, errorSelector);

static IEnumerable<TSource> _(IEnumerable<TSource> source, int count, Func<int, int, Exception> errorSelector)
{
if (source.TryAsCollectionLike() is { Count: var collectionCount }
&& collectionCount.CompareTo(count) is var comparison && comparison != 0)
Expand Down
4 changes: 2 additions & 2 deletions MoreLinq/Backsert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ public static IEnumerable<T> Backsert<T>(this IEnumerable<T> first, IEnumerable<
{
< 0 => throw new ArgumentOutOfRangeException(nameof(index), "Index cannot be negative."),
0 => first.Concat(second),
_ => _()
_ => _(first, second, index)
};

IEnumerable<T> _()
static IEnumerable<T> _(IEnumerable<T> first, IEnumerable<T> second, int index)
{
using var e = first.CountDown(index, ValueTuple.Create).GetEnumerator();

Expand Down
4 changes: 3 additions & 1 deletion MoreLinq/Batch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ public static IEnumerable<TResult> Batch<TSource, TResult>(this IEnumerable<TSou
if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
return _(source, size, resultSelector);

static IEnumerable<TResult> _(IEnumerable<TSource> source, int size, Func<TSource[], TResult> resultSelector)
{
switch (source)
{
Expand Down
70 changes: 63 additions & 7 deletions MoreLinq/Cartesian.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ public static IEnumerable<TResult> Cartesian<T1, T2, TResult>(
if (second == null) throw new ArgumentNullException(nameof(second));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
return _(first, second, resultSelector);

static IEnumerable<TResult> _(
IEnumerable<T1> first,
IEnumerable<T2> second,
Func<T1, T2, TResult> resultSelector)
{
IEnumerable<T2> secondMemo;

Expand Down Expand Up @@ -123,7 +128,13 @@ public static IEnumerable<TResult> Cartesian<T1, T2, T3, TResult>(
if (third == null) throw new ArgumentNullException(nameof(third));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
return _(first, second, third, resultSelector);

static IEnumerable<TResult> _(
IEnumerable<T1> first,
IEnumerable<T2> second,
IEnumerable<T3> third,
Func<T1, T2, T3, TResult> resultSelector)
{
IEnumerable<T2> secondMemo;
IEnumerable<T3> thirdMemo;
Expand Down Expand Up @@ -185,7 +196,14 @@ public static IEnumerable<TResult> Cartesian<T1, T2, T3, T4, TResult>(
if (fourth == null) throw new ArgumentNullException(nameof(fourth));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
return _(first, second, third, fourth, resultSelector);

static IEnumerable<TResult> _(
IEnumerable<T1> first,
IEnumerable<T2> second,
IEnumerable<T3> third,
IEnumerable<T4> fourth,
Func<T1, T2, T3, T4, TResult> resultSelector)
{
IEnumerable<T2> secondMemo;
IEnumerable<T3> thirdMemo;
Expand Down Expand Up @@ -255,7 +273,15 @@ public static IEnumerable<TResult> Cartesian<T1, T2, T3, T4, T5, TResult>(
if (fifth == null) throw new ArgumentNullException(nameof(fifth));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
return _(first, second, third, fourth, fifth, resultSelector);

static IEnumerable<TResult> _(
IEnumerable<T1> first,
IEnumerable<T2> second,
IEnumerable<T3> third,
IEnumerable<T4> fourth,
IEnumerable<T5> fifth,
Func<T1, T2, T3, T4, T5, TResult> resultSelector)
{
IEnumerable<T2> secondMemo;
IEnumerable<T3> thirdMemo;
Expand Down Expand Up @@ -333,7 +359,16 @@ public static IEnumerable<TResult> Cartesian<T1, T2, T3, T4, T5, T6, TResult>(
if (sixth == null) throw new ArgumentNullException(nameof(sixth));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
return _(first, second, third, fourth, fifth, sixth, resultSelector);

static IEnumerable<TResult> _(
IEnumerable<T1> first,
IEnumerable<T2> second,
IEnumerable<T3> third,
IEnumerable<T4> fourth,
IEnumerable<T5> fifth,
IEnumerable<T6> sixth,
Func<T1, T2, T3, T4, T5, T6, TResult> resultSelector)
{
IEnumerable<T2> secondMemo;
IEnumerable<T3> thirdMemo;
Expand Down Expand Up @@ -419,7 +454,17 @@ public static IEnumerable<TResult> Cartesian<T1, T2, T3, T4, T5, T6, T7, TResult
if (seventh == null) throw new ArgumentNullException(nameof(seventh));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
return _(first, second, third, fourth, fifth, sixth, seventh, resultSelector);

static IEnumerable<TResult> _(
IEnumerable<T1> first,
IEnumerable<T2> second,
IEnumerable<T3> third,
IEnumerable<T4> fourth,
IEnumerable<T5> fifth,
IEnumerable<T6> sixth,
IEnumerable<T7> seventh,
Func<T1, T2, T3, T4, T5, T6, T7, TResult> resultSelector)
{
IEnumerable<T2> secondMemo;
IEnumerable<T3> thirdMemo;
Expand Down Expand Up @@ -513,7 +558,18 @@ public static IEnumerable<TResult> Cartesian<T1, T2, T3, T4, T5, T6, T7, T8, TRe
if (eighth == null) throw new ArgumentNullException(nameof(eighth));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
return _(first, second, third, fourth, fifth, sixth, seventh, eighth, resultSelector);

static IEnumerable<TResult> _(
IEnumerable<T1> first,
IEnumerable<T2> second,
IEnumerable<T3> third,
IEnumerable<T4> fourth,
IEnumerable<T5> fifth,
IEnumerable<T6> sixth,
IEnumerable<T7> seventh,
IEnumerable<T8> eighth,
Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> resultSelector)
{
IEnumerable<T2> secondMemo;
IEnumerable<T3> thirdMemo;
Expand Down
9 changes: 8 additions & 1 deletion MoreLinq/Cartesian.g.tt
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,14 @@ Func<<#= string.Join(", ", from x in o.Arguments select "T" + x.Number) #>, TRes
<# } #>
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
return _(<#= string.Join(", ", from x in o.Arguments select x.Ordinal) #>, resultSelector);

static IEnumerable<TResult> _(
<# foreach (var arg in o.Arguments) { #>
IEnumerable<T<#= arg.Number #>> <#= arg.Ordinal #>,
<#
} #>
Func<<#= string.Join(", ", from x in o.Arguments select "T" + x.Number) #>, TResult> resultSelector)
{
<# foreach (var arg in o.Arguments.Skip(1)) { #>
IEnumerable<T<#= arg.Number #>> <#= arg.Ordinal #>Memo;
Expand Down
6 changes: 5 additions & 1 deletion MoreLinq/Choose.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@ public static IEnumerable<TResult> Choose<T, TResult>(this IEnumerable<T> source
if (source == null) throw new ArgumentNullException(nameof(source));
if (chooser == null) throw new ArgumentNullException(nameof(chooser));

return _(); IEnumerable<TResult> _()
return _(source, chooser);

static IEnumerable<TResult> _(
IEnumerable<T> source,
Func<T, (bool, TResult)> chooser)
{
foreach (var item in source)
{
Expand Down
4 changes: 3 additions & 1 deletion MoreLinq/CountBy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ public static IEnumerable<KeyValuePair<TKey, int>> CountBy<TSource, TKey>(this I
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));

return _(); IEnumerable<KeyValuePair<TKey, int>> _()
return _(source, keySelector, comparer);

static IEnumerable<KeyValuePair<TKey, int>> _(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer)
{
List<TKey> keys;
List<int> counts;
Expand Down
21 changes: 15 additions & 6 deletions MoreLinq/CountDown.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,15 @@ public static IEnumerable<TResult> CountDown<T, TResult>(this IEnumerable<T> sou
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return source.TryAsListLike() is { } listLike
? IterateList(listLike)
? IterateList(listLike, count, resultSelector)
: source.TryAsCollectionLike() is { } collectionLike
? IterateCollection(collectionLike)
: IterateSequence();
? IterateCollection(collectionLike, count, resultSelector)
: IterateSequence(source, count, resultSelector);

IEnumerable<TResult> IterateList(ListLike<T> list)
static IEnumerable<TResult> IterateList(
ListLike<T> list,
int count,
Func<T, int?, TResult> resultSelector)
{
var listCount = list.Count;
var countdown = Math.Min(count, listCount);
Expand All @@ -72,14 +75,20 @@ IEnumerable<TResult> IterateList(ListLike<T> list)
yield return resultSelector(list[i], listCount - i <= count ? --countdown : null);
}

IEnumerable<TResult> IterateCollection(CollectionLike<T> collection)
static IEnumerable<TResult> IterateCollection(
CollectionLike<T> collection,
int count,
Func<T, int?, TResult> resultSelector)
{
var i = collection.Count;
foreach (var item in collection)
yield return resultSelector(item, i-- <= count ? i : null);
}

IEnumerable<TResult> IterateSequence()
static IEnumerable<TResult> IterateSequence(
IEnumerable<T> source,
int count,
Func<T, int?, TResult> resultSelector)
{
var queue = new Queue<T>(Math.Max(1, count + 1));

Expand Down
7 changes: 6 additions & 1 deletion MoreLinq/DistinctBy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TS
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));

return _(); IEnumerable<TSource> _()
return _(source, keySelector, comparer);

static IEnumerable<TSource> _(
IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey>? comparer)
{
var knownKeys = new HashSet<TKey>(comparer);
foreach (var element in source)
Expand Down
8 changes: 4 additions & 4 deletions MoreLinq/EndsWith.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ public static bool EndsWith<T>(this IEnumerable<T> first, IEnumerable<T> second,
return second.TryAsCollectionLike() is { Count: var secondCount }
? first.TryAsCollectionLike() is { Count: var firstCount } && secondCount > firstCount
? false
: Impl(second, secondCount)
: Impl(secondList = second.ToList(), secondList.Count);
: Impl(first, second, secondCount, comparer)
: Impl(first, secondList = second.ToList(), secondList.Count, comparer);
#pragma warning restore IDE0075 // Simplify conditional expression

bool Impl(IEnumerable<T> snd, int count)
static bool Impl(IEnumerable<T> first, IEnumerable<T> second, int count, IEqualityComparer<T> comparer)
{
using var firstIter = first.TakeLast(count).GetEnumerator();
return snd.All(item => firstIter.MoveNext() && comparer.Equals(firstIter.Current, item));
return second.All(item => firstIter.MoveNext() && comparer.Equals(firstIter.Current, item));
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions MoreLinq/ExceptBy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,13 @@ public static IEnumerable<TSource> ExceptBy<TSource, TKey>(this IEnumerable<TSou
if (second == null) throw new ArgumentNullException(nameof(second));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));

return Impl();
return Impl(first, second, keySelector, keyComparer);

IEnumerable<TSource> Impl()
static IEnumerable<TSource> Impl(
IEnumerable<TSource> first,
IEnumerable<TSource> second,
Func<TSource, TKey> keySelector,
IEqualityComparer<TKey>? keyComparer)
{
// TODO Use ToHashSet
var keys = new HashSet<TKey>(second.Select(keySelector), keyComparer);
Expand Down
4 changes: 2 additions & 2 deletions MoreLinq/Exclude.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ public static IEnumerable<T> Exclude<T>(this IEnumerable<T> sequence, int startI
{
< 0 => throw new ArgumentOutOfRangeException(nameof(count)),
0 => sequence,
_ => _()
_ => _(sequence, startIndex, count)
};

IEnumerable<T> _()
static IEnumerable<T> _(IEnumerable<T> sequence, int startIndex, int count)
{
var index = 0;
var endIndex = startIndex + count;
Expand Down
4 changes: 2 additions & 2 deletions MoreLinq/Experimental/Async/Merge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ public static IAsyncEnumerable<T> Merge<T>(this IEnumerable<IAsyncEnumerable<T>>
if (sources is null) throw new ArgumentNullException(nameof(sources));
if (maxConcurrent <= 0) throw new ArgumentOutOfRangeException(nameof(maxConcurrent));

return Async();
return Async(sources, maxConcurrent);

async IAsyncEnumerable<T> Async([EnumeratorCancellation]CancellationToken cancellationToken = default)
static async IAsyncEnumerable<T> Async(IEnumerable<IAsyncEnumerable<T>> sources, int maxConcurrent, [EnumeratorCancellation]CancellationToken cancellationToken = default)
{
using var thisCancellationTokenSource = new CancellationTokenSource();

Expand Down
13 changes: 11 additions & 2 deletions MoreLinq/Experimental/Await.cs
Original file line number Diff line number Diff line change
Expand Up @@ -424,11 +424,20 @@ public static IAwaitQuery<TResult> AwaitCompletion<T, TTaskResult, TResult>(

return
AwaitQuery.Create(
options => Impl(options.MaxConcurrency,
options => Impl(source,
evaluator,
resultSelector,
options.MaxConcurrency,
options.Scheduler ?? TaskScheduler.Default,
options.PreserveOrder));

IEnumerable<TResult> Impl(int? maxConcurrency, TaskScheduler scheduler, bool ordered)
static IEnumerable<TResult> Impl(
IEnumerable<T> source,
Func<T, CancellationToken, Task<TTaskResult>> evaluator,
Func<T, Task<TTaskResult>, TResult> resultSelector,
int? maxConcurrency,
TaskScheduler scheduler,
bool ordered)
{
// A separate task will enumerate the source and launch tasks.
// It will post all progress as notices to the collection below.
Expand Down
7 changes: 6 additions & 1 deletion MoreLinq/Experimental/Batch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,12 @@ public static IEnumerable<TResult>
if (bucketProjectionSelector == null) throw new ArgumentNullException(nameof(bucketProjectionSelector));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));

return _(); IEnumerable<TResult> _()
return _(source, size, pool, bucketProjectionSelector, resultSelector);

static IEnumerable<TResult> _(
IEnumerable<TSource> source, int size, ArrayPool<TSource> pool,
Func<ICurrentBuffer<TSource>, IEnumerable<TBucket>> bucketProjectionSelector,
Func<IEnumerable<TBucket>, TResult> resultSelector)
{
using var batch = source.Batch(size, pool);
var bucket = bucketProjectionSelector(batch.CurrentBuffer);
Expand Down
11 changes: 10 additions & 1 deletion MoreLinq/FallbackIfEmpty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,16 @@ static IEnumerable<T> FallbackIfEmptyImpl<T>(IEnumerable<T> source,
int? count, T? fallback1, T? fallback2, T? fallback3, T? fallback4,
IEnumerable<T>? fallback)
{
return _(); IEnumerable<T> _()
return _(source, count, fallback1, fallback2, fallback3, fallback4, fallback);

static IEnumerable<T> _(
IEnumerable<T> source,
int? count,
T? fallback1,
T? fallback2,
T? fallback3,
T? fallback4,
IEnumerable<T>? fallback)
{
if (source.TryAsCollectionLike() is null or { Count: > 0 })
{
Expand Down
Loading

0 comments on commit 017e477

Please sign in to comment.