-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Range-syntax support to foreach and from in
- Loading branch information
1 parent
bf624ce
commit 35777b1
Showing
6 changed files
with
202 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
#if RANGE_SUPPORTED | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using FsCheck; | ||
using FsCheck.Xunit; | ||
using Funcky.Extensions; | ||
using Xunit; | ||
|
||
namespace Funcky.Test.Extensions | ||
{ | ||
public class RangeExtensionTest | ||
{ | ||
[Fact] | ||
public void ThereIsAMatchingSelectManyForAllCombinationsOfRangeAndEnumerable() | ||
{ | ||
var enumerable = new List<int> { 0, 1, 2 }; | ||
var expected = ExpectedFromEnumerable(enumerable).ToList(); | ||
|
||
var rangesOnly = from x in 0..3 | ||
from y in 0..3 | ||
select x + y; | ||
|
||
var firstRange = from x in 0..3 | ||
from y in enumerable | ||
select x + y; | ||
|
||
var lastRange = from x in enumerable | ||
from y in 0..3 | ||
select x + y; | ||
|
||
Assert.Equal(expected, rangesOnly); | ||
Assert.Equal(expected, firstRange); | ||
Assert.Equal(expected, lastRange); | ||
} | ||
|
||
[Fact] | ||
public void YouCanUseARangeInForeachSyntax() | ||
{ | ||
var expected = new List<int> { -2, -1, 0, 1 }; | ||
var list = new List<int>(); | ||
|
||
foreach (var index in ^2..2) | ||
{ | ||
list.Add(index); | ||
} | ||
|
||
Assert.Equal(expected, list); | ||
} | ||
|
||
[Fact] | ||
public void ADownToRangeWorksAsExpected() | ||
{ | ||
var expected = new List<int> { 7, 6, 5, 4, 3, 2, 1, 0, -1 }; | ||
var list = new List<int>(); | ||
|
||
foreach (var index in 7..^2) | ||
{ | ||
list.Add(index); | ||
} | ||
|
||
Assert.Equal(expected, list); | ||
} | ||
|
||
[Property] | ||
public Property AnyRangeProducedAValidEnumeration(NonNegativeInt start, bool signStart, NonNegativeInt end, bool signEnd) | ||
{ | ||
var range = new Range(new Index(start.Get, signStart), new Index(end.Get, signEnd)); | ||
|
||
var fromRange = from index in range select index; | ||
|
||
return fromRange.SequenceEqual(CreateRange(range)).ToProperty(); | ||
} | ||
|
||
private static IEnumerable<int> ExpectedFromEnumerable(List<int> enumerable) | ||
=> from x in enumerable | ||
from y in enumerable | ||
select x + y; | ||
|
||
private static IEnumerable<int> CreateRange(Range range) | ||
=> ToInt(range.Start) < ToInt(range.End) | ||
? Enumerable.Range(ToInt(range.Start), GetDistance(range)) | ||
: Enumerable.Range(ToInt(range.End) + 1, GetDistance(range)).Reverse(); | ||
|
||
private static int GetDistance(Range range) | ||
=> GetDistance(range.Start, range.End); | ||
|
||
private static int GetDistance(Index start, Index end) | ||
=> GetDistance(ToInt(start), ToInt(end)); | ||
|
||
private static int GetDistance(int start, int end) | ||
=> start > end | ||
? start - end | ||
: end - start; | ||
|
||
private static int ToInt(Index index) | ||
=> index.IsFromEnd | ||
? -index.Value | ||
: index.Value; | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
#if RANGE_SUPPORTED | ||
using System.Linq; | ||
using Funcky.Internal; | ||
|
||
namespace Funcky.Extensions | ||
{ | ||
public static class RangeExtensions | ||
{ | ||
public static IEnumerator<int> GetEnumerator(this Range range) | ||
=> IterateRange(ToInt(range.Start), ToInt(range.End)); | ||
|
||
[Pure] | ||
public static IEnumerable<TResult> Select<TResult>(this Range source, Func<int, TResult> selector) | ||
=> source.ToRangeEnumerable().Select(selector); | ||
|
||
[Pure] | ||
public static IEnumerable<TResult> SelectMany<TResult>(this Range source, Func<int, IEnumerable<TResult>> selector) | ||
=> source.ToRangeEnumerable().SelectMany(selector); | ||
|
||
[Pure] | ||
public static IEnumerable<TResult> SelectMany<TItem, TResult>(this Range source, Func<int, IEnumerable<TItem>> collectionSelector, Func<int, TItem, TResult> resultSelector) | ||
=> source.ToRangeEnumerable().SelectMany(collectionSelector, resultSelector); | ||
|
||
[Pure] | ||
public static IEnumerable<TResult> SelectMany<TResult>(this Range source, Func<int, Range> rangeSelector, Func<int, int, TResult> resultSelector) | ||
=> source.ToRangeEnumerable().SelectMany(TransformSelector(rangeSelector), resultSelector); | ||
|
||
[Pure] | ||
public static IEnumerable<TResult> SelectMany<TItem, TResult>(this IEnumerable<TItem> source, Func<TItem, Range> rangeSelector, Func<TItem, int, TResult> resultSelector) | ||
=> source.SelectMany(TransformSelector(rangeSelector), resultSelector); | ||
|
||
private static IEnumerator<int> IterateRange(int start, int end) | ||
{ | ||
for (var index = start; CanAdvance(index, end); index = Advance(index, GetDirection(start, end))) | ||
{ | ||
yield return index; | ||
} | ||
} | ||
|
||
private static RangeEnumerable ToRangeEnumerable(this Range source) | ||
=> new(source); | ||
|
||
private static Func<TItem, RangeEnumerable> TransformSelector<TItem>(Func<TItem, Range> rangeSelector) | ||
=> value | ||
=> rangeSelector(value).ToRangeEnumerable(); | ||
|
||
private static int Advance(int index, int direction) | ||
=> index + direction; | ||
|
||
private static bool CanAdvance(int index, int end) | ||
=> index != end; | ||
|
||
private static int GetDirection(int start, int end) | ||
=> end.CompareTo(start); | ||
|
||
private static int ToInt(Index index) | ||
=> index.IsFromEnd | ||
? -index.Value | ||
: index.Value; | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#if RANGE_SUPPORTED | ||
using System.Collections; | ||
|
||
namespace Funcky.Internal | ||
{ | ||
internal class RangeEnumerable : IEnumerable<int> | ||
{ | ||
private readonly Range _range; | ||
|
||
public RangeEnumerable(Range range) | ||
=> _range = range; | ||
|
||
public IEnumerator<int> GetEnumerator() | ||
=> _range.GetEnumerator(); | ||
|
||
IEnumerator IEnumerable.GetEnumerator() | ||
=> GetEnumerator(); | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,13 @@ | ||
#nullable enable | ||
Funcky.Extensions.QueueExtensions | ||
Funcky.Extensions.RangeExtensions | ||
static Funcky.Extensions.QueueExtensions.DequeueOrNone<TItem>(this System.Collections.Concurrent.ConcurrentQueue<TItem>! concurrentQueue) -> Funcky.Monads.Option<TItem> | ||
static Funcky.Extensions.QueueExtensions.DequeueOrNone<TItem>(this System.Collections.Generic.Queue<TItem>! queue) -> Funcky.Monads.Option<TItem> | ||
static Funcky.Extensions.QueueExtensions.PeekOrNone<TItem>(this System.Collections.Concurrent.ConcurrentQueue<TItem>! concurrentQueue) -> Funcky.Monads.Option<TItem> | ||
static Funcky.Extensions.QueueExtensions.PeekOrNone<TItem>(this System.Collections.Generic.Queue<TItem>! queue) -> Funcky.Monads.Option<TItem> | ||
static Funcky.Extensions.RangeExtensions.GetEnumerator(this System.Range range) -> System.Collections.Generic.IEnumerator<int>! | ||
static Funcky.Extensions.RangeExtensions.Select<TResult>(this System.Range source, System.Func<int, TResult>! selector) -> System.Collections.Generic.IEnumerable<TResult>! | ||
static Funcky.Extensions.RangeExtensions.SelectMany<TItem, TResult>(this System.Collections.Generic.IEnumerable<TItem>! source, System.Func<TItem, System.Range>! rangeSelector, System.Func<TItem, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>! | ||
static Funcky.Extensions.RangeExtensions.SelectMany<TItem, TResult>(this System.Range source, System.Func<int, System.Collections.Generic.IEnumerable<TItem>!>! collectionSelector, System.Func<int, TItem, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>! | ||
static Funcky.Extensions.RangeExtensions.SelectMany<TResult>(this System.Range source, System.Func<int, System.Collections.Generic.IEnumerable<TResult>!>! selector) -> System.Collections.Generic.IEnumerable<TResult>! | ||
static Funcky.Extensions.RangeExtensions.SelectMany<TResult>(this System.Range source, System.Func<int, System.Range>! rangeSelector, System.Func<int, int, TResult>! resultSelector) -> System.Collections.Generic.IEnumerable<TResult>! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters