diff --git a/MoreLinq.Test/UnfoldTest.cs b/MoreLinq.Test/UnfoldTest.cs index ef2a3ee1b..aecc0aa29 100644 --- a/MoreLinq.Test/UnfoldTest.cs +++ b/MoreLinq.Test/UnfoldTest.cs @@ -17,80 +17,108 @@ namespace MoreLinq.Test { + using System.Collections.Generic; + using System.Runtime.CompilerServices; using NUnit.Framework; [TestFixture] public class UnfoldTest { - [Test] - public void UnfoldInfiniteSequence() - { - var result = MoreEnumerable.Unfold(1, x => (Result: x, State: x + 1), + static IEnumerable + TestCaseData(IEnumerable source1, + IEnumerable source2, + [CallerMemberName] string? callerMemberName = null) => + [ + new(source1) { TestName = $"{callerMemberName}(generator)" }, + new(source2) { TestName = $"{callerMemberName}(state, generator, predicate, stateSelector, resultSelector)" }, + ]; + + static IEnumerable UnfoldInfiniteSequenceData() => + TestCaseData(MoreEnumerable.Unfold(1, x => (true, (Result: x, State: x + 1))), + MoreEnumerable.Unfold(1, x => (Result: x, State: x + 1), _ => true, e => e.State, - e => e.Result) - .Take(100); + e => e.Result)); + [Test] + [TestCaseSource(nameof(UnfoldInfiniteSequenceData))] + public void UnfoldInfiniteSequence(IEnumerable unfold) + { + var result = unfold.Take(100); var expectations = MoreEnumerable.Generate(1, x => x + 1).Take(100); - Assert.That(result, Is.EqualTo(expectations)); } - [Test] - public void UnfoldFiniteSequence() - { - var result = MoreEnumerable.Unfold(1, x => (Result: x, State: x + 1), + static IEnumerable UnfoldFiniteSequenceData() => + TestCaseData(MoreEnumerable.Unfold(1, x => x <= 100 ? (true, (Result: x, State: x + 1)) : default), + MoreEnumerable.Unfold(1, x => (Result: x, State: x + 1), e => e.Result <= 100, e => e.State, - e => e.Result); + e => e.Result)); + [Test] + [TestCaseSource(nameof(UnfoldFiniteSequenceData))] + public void UnfoldFiniteSequence(IEnumerable unfold) + { + var result = unfold; var expectations = MoreEnumerable.Generate(1, x => x + 1).Take(100); - Assert.That(result, Is.EqualTo(expectations)); } [Test] public void UnfoldIsLazy() { + _ = MoreEnumerable.Unfold(0, BreakingFunc.Of()); + _ = MoreEnumerable.Unfold(0, BreakingFunc.Of(), BreakingFunc.Of<(int, int), bool>(), BreakingFunc.Of<(int, int), int>(), BreakingFunc.Of<(int, int), int>()); } - - [Test] - public void UnfoldSingleElementSequence() - { - var result = MoreEnumerable.Unfold(0, x => (Result: x, State: x + 1), + static IEnumerable UnfoldSingleElementSequenceData() => + TestCaseData(MoreEnumerable.Unfold(0, x => x == 0 ? (true, (Result: x, State: x + 1)) : default), + MoreEnumerable.Unfold(0, x => (Result: x, State: x + 1), x => x.Result == 0, e => e.State, - e => e.Result); + e => e.Result)); + [Test] + [TestCaseSource(nameof(UnfoldSingleElementSequenceData))] + public void UnfoldSingleElementSequence(IEnumerable unfold) + { + var result = unfold; var expectations = new[] { 0 }; - Assert.That(result, Is.EqualTo(expectations)); } - [Test] - public void UnfoldEmptySequence() - { - var result = MoreEnumerable.Unfold(0, x => (Result: x, State: x + 1), + static IEnumerable UnfoldEmptySequenceData() => + TestCaseData(MoreEnumerable.Unfold(0, x => x < 0 ? (true, (Result: x, State: x + 1)) : default), + MoreEnumerable.Unfold(0, x => (Result: x, State: x + 1), x => x.Result < 0, e => e.State, - e => e.Result); + e => e.Result)); + + [Test] + [TestCaseSource(nameof(UnfoldEmptySequenceData))] + public void UnfoldEmptySequence(IEnumerable unfold) + { + var result = unfold; Assert.That(result, Is.Empty); } + static IEnumerable UnfoldReiterationsReturnsSameResultData() => + TestCaseData(MoreEnumerable.Unfold(1, n => (true, (Result: n, State: n + 1))), + MoreEnumerable.Unfold(1, n => (Result: n, Next: n + 1), + _ => true, + n => n.Next, + n => n.Result)); + [Test(Description = "https://github.com/morelinq/MoreLINQ/issues/990")] - public void UnfoldReiterationsReturnsSameResult() + [TestCaseSource(nameof(UnfoldReiterationsReturnsSameResultData))] + public void UnfoldReiterationsReturnsSameResult(IEnumerable unfold) { - var xs = MoreEnumerable.Unfold(1, n => (Result: n, Next: n + 1), - _ => true, - n => n.Next, - n => n.Result) - .Take(5); - + var xs = unfold.Take(5); xs.AssertSequenceEqual(1, 2, 3, 4, 5); xs.AssertSequenceEqual(1, 2, 3, 4, 5); } diff --git a/MoreLinq/PublicAPI/net6.0/PublicAPI.Unshipped.txt b/MoreLinq/PublicAPI/net6.0/PublicAPI.Unshipped.txt index 7dc5c5811..f9305f8f5 100644 --- a/MoreLinq/PublicAPI/net6.0/PublicAPI.Unshipped.txt +++ b/MoreLinq/PublicAPI/net6.0/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ #nullable enable +static MoreLinq.MoreEnumerable.Unfold(TState state, System.Func! generator) -> System.Collections.Generic.IEnumerable! diff --git a/MoreLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/MoreLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt index 7dc5c5811..f9305f8f5 100644 --- a/MoreLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/MoreLinq/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ #nullable enable +static MoreLinq.MoreEnumerable.Unfold(TState state, System.Func! generator) -> System.Collections.Generic.IEnumerable! diff --git a/MoreLinq/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/MoreLinq/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index 7dc5c5811..f9305f8f5 100644 --- a/MoreLinq/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/MoreLinq/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ #nullable enable +static MoreLinq.MoreEnumerable.Unfold(TState state, System.Func! generator) -> System.Collections.Generic.IEnumerable! diff --git a/MoreLinq/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt b/MoreLinq/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt index 7dc5c5811..f9305f8f5 100644 --- a/MoreLinq/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt +++ b/MoreLinq/PublicAPI/netstandard2.1/PublicAPI.Unshipped.txt @@ -1 +1,2 @@ #nullable enable +static MoreLinq.MoreEnumerable.Unfold(TState state, System.Func! generator) -> System.Collections.Generic.IEnumerable! diff --git a/MoreLinq/Unfold.cs b/MoreLinq/Unfold.cs index 380a10858..df3bb44ee 100644 --- a/MoreLinq/Unfold.cs +++ b/MoreLinq/Unfold.cs @@ -22,6 +22,44 @@ namespace MoreLinq static partial class MoreEnumerable { + /// + /// Returns a sequence generated by applying a state to the generator function, which + /// optionally determines the next state and element of the sequence. + /// + /// Type of state elements. + /// The type of the elements of the result sequence. + /// The initial state. + /// + /// Function that takes a state and computes a Boolean coupled with the element of the + /// sequence to yield and the next state. The Boolean value indicates whether the sequence + /// should continue or not. If false, the sequence ends and the remaining bits, like + /// element and next state are ignored. + /// + /// + /// A sequence containing the results generated by the + /// function. + /// + /// This operator uses deferred execution and streams its results. + /// + /// + /// s.Curr < 100 ? (true, s.Curr, (s.Next, s.Curr + s.Next)) : default); + /// ]]> + /// The fibonacciNumbersLowerThan100 will be a sequence that will yield + /// { 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 }. + /// + + public static IEnumerable Unfold( + TState state, + Func generator) + { + if (generator == null) throw new ArgumentNullException(nameof(generator)); + + return Unfold(state, generator, e => e.Continue, e => e.Generated.State, e => e.Generated.Result); + } + /// /// Returns a sequence generated by applying a state to the generator function, /// and from its result, determines if the sequence should have a next element, its value, diff --git a/README.md b/README.md index 2a897f211..2b9b714b7 100644 --- a/README.md +++ b/README.md @@ -707,6 +707,8 @@ Returns a sequence generated by applying a state to the generator function, and from its result, determines if the sequence should have a next element and its value, and the next state in the recursive call. +This method has 2 overloads. + ### Window Processes a sequence into a series of subsequences representing a windowed