Skip to content

Commit b0e5591

Browse files
authored
Merge pull request #773 from polyadic/usability-of-repeat-range
2 parents 6df18de + ce7a408 commit b0e5591

File tree

9 files changed

+140
-2
lines changed

9 files changed

+140
-2
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using FsCheck;
2+
using FsCheck.Xunit;
3+
using Funcky.Test.TestUtils;
4+
5+
namespace Funcky.Test;
6+
7+
public sealed class CycleMaterializedTest
8+
{
9+
[Fact]
10+
public void IsEnumeratedLazily()
11+
{
12+
var doNotEnumerate = new FailOnEnumerateReadOnlyCollection<object>(Count: 1);
13+
_ = Sequence.CycleMaterialized(doNotEnumerate);
14+
}
15+
16+
[Fact]
17+
public void CyclingAnEmptySetThrowsAnException()
18+
=> Assert.Throws<InvalidOperationException>(CycleEmptySequence);
19+
20+
[Property]
21+
public Property CanProduceArbitraryManyItems(NonEmptySet<int> sequence, PositiveInt arbitraryElements)
22+
{
23+
var cycleRange = Sequence.CycleMaterialized(sequence.Get.Materialize());
24+
25+
return (cycleRange.Take(arbitraryElements.Get).Count() == arbitraryElements.Get)
26+
.ToProperty();
27+
}
28+
29+
[Property]
30+
public Property RepeatsTheElementsArbitraryManyTimes(NonEmptySet<int> sequence, PositiveInt arbitraryElements)
31+
{
32+
var cycleRange = Sequence.CycleMaterialized(sequence.Get.Materialize());
33+
34+
return cycleRange
35+
.IsSequenceRepeating(sequence.Get)
36+
.NTimes(arbitraryElements.Get)
37+
.ToProperty();
38+
}
39+
40+
private static void CycleEmptySequence()
41+
{
42+
var cycledRange = Sequence.CycleMaterialized(Array.Empty<int>());
43+
using var enumerator = cycledRange.GetEnumerator();
44+
45+
enumerator.MoveNext();
46+
}
47+
}

Funcky.Test/Sequence/CycleRangeTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ public void CycleRangeIsEnumeratedLazily()
1515
}
1616

1717
[Fact]
18-
public void CyclingAnEmptySetThrowsAnArgumentException()
19-
=> Assert.Throws<InvalidOperationException>(CycleEmptySequence);
18+
public void CyclingAnEmptySetThrowsAnException()
19+
=> Assert.Throws<InvalidOperationException>(CycleEmptySequence);
2020

2121
[Property]
2222
public Property CycleRangeCanProduceArbitraryManyItems(NonEmptySet<int> sequence, PositiveInt arbitraryElements)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using FsCheck;
2+
using FsCheck.Xunit;
3+
using Funcky.Test.TestUtils;
4+
5+
namespace Funcky.Test;
6+
7+
public sealed class RepeatMaterializedTest
8+
{
9+
[Fact]
10+
public void IsEnumeratedLazily()
11+
{
12+
var doNotEnumerate = new FailOnEnumerateReadOnlyCollection<object>(Count: 0);
13+
_ = Sequence.RepeatMaterialized(doNotEnumerate, 2);
14+
}
15+
16+
[Property]
17+
public Property ARepeatedEmptySequenceIsStillEmpty(NonNegativeInt count)
18+
{
19+
var repeated = Sequence.RepeatMaterialized(Array.Empty<object>(), count.Get);
20+
return (!repeated.Any()).ToProperty();
21+
}
22+
23+
[Property]
24+
public Property TheLengthOfTheGeneratedSequenceIsCorrect(List<int> list, NonNegativeInt count)
25+
{
26+
var repeatRange = Sequence.RepeatMaterialized(list, count.Get);
27+
28+
var materialized = repeatRange.ToList();
29+
30+
return (materialized.Count == list.Count * count.Get).ToProperty();
31+
}
32+
33+
[Property]
34+
public Property TheSequenceRepeatsTheGivenNumberOfTimes(List<int> list, NonNegativeInt count)
35+
{
36+
var repeatRange = Sequence.RepeatMaterialized(list, count.Get);
37+
38+
return repeatRange
39+
.IsSequenceRepeating(list)
40+
.NTimes(count.Get)
41+
.ToProperty();
42+
}
43+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.Collections;
2+
using Xunit.Sdk;
3+
4+
namespace Funcky.Test.TestUtils;
5+
6+
internal record FailOnEnumerateReadOnlyCollection<T>(int Count) : IReadOnlyCollection<T>
7+
{
8+
public IEnumerator<T> GetEnumerator() => throw new XunitException("Should not be enumerated");
9+
10+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
11+
}

Funcky/PublicAPI.Unshipped.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ static Funcky.DownCast<TResult>.From<TItem>(Funcky.Monads.Result<TItem!> result)
55
static Funcky.DownCast<TResult>.From<TLeft, TRight>(Funcky.Monads.Either<TLeft, TRight!> either, System.Func<TLeft>! failedCast) -> Funcky.Monads.Either<TLeft, TResult!>
66
static Funcky.Extensions.ParseExtensions.ParseTypeNameOrNone(this System.ReadOnlySpan<char> candidate, System.Reflection.Metadata.TypeNameParseOptions? options = null) -> Funcky.Monads.Option<System.Reflection.Metadata.TypeName!>
77
static Funcky.Extensions.ParseExtensions.ParseAssemblyNameInfoOrNone(this System.ReadOnlySpan<char> candidate) -> Funcky.Monads.Option<System.Reflection.Metadata.AssemblyNameInfo!>
8+
static Funcky.Sequence.CycleMaterialized<TSource>(System.Collections.Generic.IReadOnlyCollection<TSource>! source) -> System.Collections.Generic.IEnumerable<TSource>!
9+
static Funcky.Sequence.RepeatMaterialized<TSource>(System.Collections.Generic.IReadOnlyCollection<TSource>! source, int count) -> System.Collections.Generic.IEnumerable<TSource>!
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Funcky;
2+
3+
public static partial class Sequence
4+
{
5+
/// <summary>
6+
/// Generates a sequence that contains the same sequence of elements over and over again as an endless generator.
7+
/// </summary>
8+
/// <typeparam name="TSource">Type of the elements to be cycled.</typeparam>
9+
/// <param name="source">The sequence of elements which are cycled. Throws an exception if the sequence is empty.</param>
10+
/// <returns>Returns an infinite IEnumerable repeating the same sequence of elements.</returns>
11+
/// <remarks>Use <see cref="CycleRange{TSource}"/> if you need to cycle a lazy sequence.</remarks>
12+
[Pure]
13+
public static IEnumerable<TSource> CycleMaterialized<TSource>(IReadOnlyCollection<TSource> source)
14+
=> source.Count > 0
15+
? Cycle(source).SelectMany(Identity)
16+
: throw new InvalidOperationException("you cannot cycle an empty enumerable");
17+
}

Funcky/Sequence/Sequence.CycleRange.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public static partial class Sequence
1111
/// <typeparam name="TSource">Type of the elements to be cycled.</typeparam>
1212
/// <param name="source">The sequence of elements which are cycled. Throws an exception if the sequence is empty.</param>
1313
/// <returns>Returns an infinite IEnumerable repeating the same sequence of elements.</returns>
14+
/// <remarks>Use <see cref="CycleMaterialized{TSource}"/> if you need to cycle an already materialized sequence.</remarks>
1415
[Pure]
1516
public static IBuffer<TSource> CycleRange<TSource>(IEnumerable<TSource> source)
1617
=> CycleBuffer.Create(source);
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
namespace Funcky;
2+
3+
public static partial class Sequence
4+
{
5+
/// <summary>
6+
/// Generates a sequence that contains the same sequence of elements the given number of times.
7+
/// </summary>
8+
/// <typeparam name="TSource">Type of the elements to be repeated.</typeparam>
9+
/// <param name="source">The sequence of elements to be repeated.</param>
10+
/// <param name="count">The number of times to repeat the value in the generated sequence.</param>
11+
/// <returns>Returns an infinite IEnumerable cycling through the same elements.</returns>
12+
/// <remarks>Use <see cref="RepeatRange{TSource}"/> if you need to cycle a lazy sequence.</remarks>
13+
[Pure]
14+
public static IEnumerable<TSource> RepeatMaterialized<TSource>(IReadOnlyCollection<TSource> source, int count)
15+
=> Enumerable.Repeat(source, count).SelectMany(Identity);
16+
}

Funcky/Sequence/Sequence.RepeatRange.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public static partial class Sequence
99
/// <param name="source">The sequence of elements to be repeated.</param>
1010
/// <param name="count">The number of times to repeat the value in the generated sequence.</param>
1111
/// <returns>Returns an infinite IEnumerable cycling through the same elements.</returns>
12+
/// <remarks>Use <see cref="RepeatMaterialized{TSource}"/> if you need to cycle an already materialized sequence.</remarks>
1213
[Pure]
1314
public static IBuffer<TSource> RepeatRange<TSource>(IEnumerable<TSource> source, int count)
1415
=> CycleBuffer.Create(source, count);

0 commit comments

Comments
 (0)