Skip to content

Commit 0b25ccd

Browse files
committed
Implement TaskSeq.skipWhile, skipWhileAsync, skipWhileInclusive and skipWhileInclusiveAsync
1 parent de928fb commit 0b25ccd

File tree

3 files changed

+142
-13
lines changed

3 files changed

+142
-13
lines changed

src/FSharp.Control.TaskSeq/TaskSeq.fs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,10 @@ type TaskSeq private () =
297297
static member takeWhileAsync predicate source = Internal.takeWhile Exclusive (PredicateAsync predicate) source
298298
static member takeWhileInclusive predicate source = Internal.takeWhile Inclusive (Predicate predicate) source
299299
static member takeWhileInclusiveAsync predicate source = Internal.takeWhile Inclusive (PredicateAsync predicate) source
300+
static member skipWhile predicate source = Internal.skipWhile Exclusive (Predicate predicate) source
301+
static member skipWhileAsync predicate source = Internal.skipWhile Exclusive (PredicateAsync predicate) source
302+
static member skipWhileInclusive predicate source = Internal.skipWhile Inclusive (Predicate predicate) source
303+
static member skipWhileInclusiveAsync predicate source = Internal.skipWhile Inclusive (PredicateAsync predicate) source
300304

301305
static member tryPick chooser source = Internal.tryPick (TryPick chooser) source
302306
static member tryPickAsync chooser source = Internal.tryPick (TryPickAsync chooser) source

src/FSharp.Control.TaskSeq/TaskSeq.fsi

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -830,10 +830,10 @@ type TaskSeq =
830830
static member takeWhile: predicate: ('T -> bool) -> source: TaskSeq<'T> -> TaskSeq<'T>
831831

832832
/// <summary>
833-
/// Returns a sequence that, when iterated, yields elements of the underlying sequence while the
833+
/// Returns a task sequence that, when iterated, yields elements of the underlying sequence while the
834834
/// given asynchronous function <paramref name="predicate" /> returns <see cref="true" />, and then returns no further elements.
835835
/// The first element where the predicate returns <see cref="false" /> is not included in the resulting sequence
836-
/// (see also <see cref="TaskSeq.takeWhileInclusive" />).
836+
/// (see also <see cref="TaskSeq.takeWhileInclusiveAsync" />).
837837
/// If <paramref name="predicate" /> is synchronous, consider using <see cref="TaskSeq.takeWhile" />.
838838
/// </summary>
839839
///
@@ -844,7 +844,7 @@ type TaskSeq =
844844
static member takeWhileAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> TaskSeq<'T>
845845

846846
/// <summary>
847-
/// Returns a sequence that, when iterated, yields elements of the underlying sequence until the given
847+
/// Returns a task sequence that, when iterated, yields elements of the underlying sequence until the given
848848
/// function <paramref name="predicate" /> returns <see cref="false" />, returns that element
849849
/// and then returns no further elements (see also <see cref="TaskSeq.takeWhile" />). This function returns
850850
/// at least one element of a non-empty sequence, or the empty task sequence if the input is empty.
@@ -858,9 +858,9 @@ type TaskSeq =
858858
static member takeWhileInclusive: predicate: ('T -> bool) -> source: TaskSeq<'T> -> TaskSeq<'T>
859859

860860
/// <summary>
861-
/// Returns a sequence that, when iterated, yields elements of the underlying sequence until the given
861+
/// Returns a task sequence that, when iterated, yields elements of the underlying sequence until the given
862862
/// asynchronous function <paramref name="predicate" /> returns <see cref="false" />, returns that element
863-
/// and then returns no further elements (see also <see cref="TaskSeq.takeWhile" />). This function returns
863+
/// and then returns no further elements (see also <see cref="TaskSeq.takeWhileAsync" />). This function returns
864864
/// at least one element of a non-empty sequence, or the empty task sequence if the input is empty.
865865
/// If <paramref name="predicate" /> is synchronous, consider using <see cref="TaskSeq.takeWhileInclusive" />.
866866
/// </summary>
@@ -871,6 +871,62 @@ type TaskSeq =
871871
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
872872
static member takeWhileInclusiveAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> TaskSeq<'T>
873873

874+
/// <summary>
875+
/// Returns a task sequence that, when iterated, skips elements of the underlying sequence while the
876+
/// given function <paramref name="predicate" /> returns <see cref="true" />, and then yields the remaining
877+
/// elements. The first element where the predicate returns <see cref="false" /> is returned, which means that this
878+
/// function will skip 0 or more elements (see also <see cref="TaskSeq.skipWhileInclusive" />).
879+
/// If <paramref name="predicate" /> is asynchronous, consider using <see cref="TaskSeq.skipWhileAsync" />.
880+
/// </summary>
881+
///
882+
/// <param name="predicate">A function that evaluates to false when no more items should be skipped.</param>
883+
/// <param name="source">The input task sequence.</param>
884+
/// <returns>The resulting task sequence.</returns>
885+
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
886+
static member skipWhile: predicate: ('T -> bool) -> source: TaskSeq<'T> -> TaskSeq<'T>
887+
888+
/// <summary>
889+
/// Returns a task sequence that, when iterated, skips elements of the underlying sequence while the
890+
/// given asynchronous function <paramref name="predicate" /> returns <see cref="true" />, and then yields the
891+
/// remaining elements. The first element where the predicate returns <see cref="false" /> is returned, which
892+
/// means that this function will skip 0 or more elements (see also <see cref="TaskSeq.skipWhileInclusive" />).
893+
/// If <paramref name="predicate" /> is synchronous, consider using <see cref="TaskSeq.skipWhile" />.
894+
/// </summary>
895+
///
896+
/// <param name="predicate">An asynchronous function that evaluates to false when no more items should be skipped.</param>
897+
/// <param name="source">The input task sequence.</param>
898+
/// <returns>The resulting task sequence.</returns>
899+
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
900+
static member skipWhileAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> TaskSeq<'T>
901+
902+
/// <summary>
903+
/// Returns a task sequence that, when iterated, skips elements of the underlying sequence until the given
904+
/// function <paramref name="predicate" /> returns <see cref="false" />, also skips that element
905+
/// and then yields the remaining elements (see also <see cref="TaskSeq.skipWhile" />). This function skips
906+
/// at least one element of a non-empty sequence, or returns the empty task sequence if the input is empty.
907+
/// If <paramref name="predicate" /> is asynchronous, consider using <see cref="TaskSeq.skipWhileInclusiveAsync" />.
908+
/// </summary>`
909+
///
910+
/// <param name="predicate">A function that evaluates to false when no more items should be skipped.</param>
911+
/// <param name="source">The input task sequence.</param>
912+
/// <returns>The resulting task sequence.</returns>
913+
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
914+
static member skipWhileInclusive: predicate: ('T -> bool) -> source: TaskSeq<'T> -> TaskSeq<'T>
915+
916+
/// <summary>
917+
/// Returns a task sequence that, when iterated, skips elements of the underlying sequence until the given
918+
/// function <paramref name="predicate" /> returns <see cref="false" />, also skips that element
919+
/// and then yields the remaining elements (see also <see cref="TaskSeq.skipWhileAsync" />). This function skips
920+
/// at least one element of a non-empty sequence, or returns the empty task sequence if the input is empty.
921+
/// If <paramref name="predicate" /> is synchronous, consider using <see cref="TaskSeq.skipWhileInclusive" />.
922+
/// </summary>
923+
///
924+
/// <param name="predicate">An asynchronous function that evaluates to false when no more items should be skipped.</param>
925+
/// <param name="source">The input task sequence.</param>
926+
/// <returns>The resulting task sequence.</returns>
927+
/// <exception cref="T:ArgumentNullException">Thrown when the input task sequence is null.</exception>
928+
static member skipWhileInclusiveAsync: predicate: ('T -> #Task<bool>) -> source: TaskSeq<'T> -> TaskSeq<'T>
929+
874930
/// <summary>
875931
/// Applies the given function <paramref name="chooser" /> to successive elements, returning the first result where
876932
/// the function returns <see cref="Some(x)" />.

src/FSharp.Control.TaskSeq/TaskSeqInternal.fs

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ type internal AsyncEnumStatus =
1313

1414
[<Struct>]
1515
type internal WhileKind =
16-
/// The item under test is included even if false
16+
/// The item under test is included (or skipped) even if false
1717
| Inclusive
18-
/// The item under test is always excluded
18+
/// The item under test is always excluded (or not skipped)
1919
| Exclusive
2020

2121
[<Struct>]
@@ -731,55 +731,124 @@ module internal TaskSeqInternal =
731731

732732
taskSeq {
733733
use e = source.GetAsyncEnumerator CancellationToken.None
734-
let! step = e.MoveNextAsync()
735-
let mutable more = step
734+
let! moveFirst = e.MoveNextAsync()
735+
let mutable more = moveFirst
736736

737737
match whileKind, predicate with
738-
| Exclusive, Predicate predicate ->
738+
| Exclusive, Predicate predicate -> // takeWhile
739739
while more do
740740
let value = e.Current
741741
more <- predicate value
742742

743743
if more then
744+
// yield ONLY if predicate is true
744745
yield value
745746
let! ok = e.MoveNextAsync()
746747
more <- ok
747748

748-
| Inclusive, Predicate predicate ->
749+
| Inclusive, Predicate predicate -> // takeWhileInclusive
749750
while more do
750751
let value = e.Current
751752
more <- predicate value
752753

754+
// yield, regardless of result of predicate
753755
yield value
754756

755757
if more then
756758
let! ok = e.MoveNextAsync()
757759
more <- ok
758760

759-
| Exclusive, PredicateAsync predicate ->
761+
| Exclusive, PredicateAsync predicate -> // takeWhileAsync
760762
while more do
761763
let value = e.Current
762764
let! passed = predicate value
763765
more <- passed
764766

765767
if more then
768+
// yield ONLY if predicate is true
766769
yield value
767770
let! ok = e.MoveNextAsync()
768771
more <- ok
769772

770-
| Inclusive, PredicateAsync predicate ->
773+
| Inclusive, PredicateAsync predicate -> // takeWhileInclusiveAsync
771774
while more do
772775
let value = e.Current
773776
let! passed = predicate value
774777
more <- passed
775778

779+
// yield regardless of predicate
776780
yield value
777781

778782
if more then
779783
let! ok = e.MoveNextAsync()
780784
more <- ok
781785
}
782786

787+
let skipWhile whileKind predicate (source: TaskSeq<_>) =
788+
checkNonNull (nameof source) source
789+
790+
taskSeq {
791+
use e = source.GetAsyncEnumerator CancellationToken.None
792+
let! moveFirst = e.MoveNextAsync()
793+
let mutable more = moveFirst
794+
795+
match whileKind, predicate with
796+
| Exclusive, Predicate predicate -> // skipWhile
797+
while more && predicate e.Current do
798+
let! ok = e.MoveNextAsync()
799+
more <- ok
800+
801+
if more then
802+
// yield the last one where the predicate was false
803+
// (this ensures we skip 0 or more)
804+
yield e.Current
805+
806+
while! e.MoveNextAsync() do // get the rest
807+
yield e.Current
808+
809+
| Inclusive, Predicate predicate -> // skipWhileInclusive
810+
while more && predicate e.Current do
811+
let! ok = e.MoveNextAsync()
812+
more <- ok
813+
814+
if more then
815+
// yield the rest (this ensures we skip 1 or more)
816+
while! e.MoveNextAsync() do
817+
yield e.Current
818+
819+
| Exclusive, PredicateAsync predicate -> // skipWhileAsync
820+
while more do
821+
let! passed = predicate e.Current
822+
more <- passed
823+
824+
if more then
825+
let! ok = e.MoveNextAsync()
826+
more <- ok
827+
828+
// yield the rest
829+
if more then
830+
// yield the last one where the predicate was false
831+
// (this ensures we skip 0 or more)
832+
yield e.Current
833+
834+
while! e.MoveNextAsync() do
835+
yield e.Current
836+
837+
| Inclusive, PredicateAsync predicate -> // skipWhileInclusiveAsync
838+
while more do
839+
let! passed = predicate e.Current
840+
more <- passed
841+
842+
if more then
843+
let! ok = e.MoveNextAsync()
844+
more <- ok
845+
846+
if more then
847+
// yield the rest (this ensures we skip 1 or more)
848+
while! e.MoveNextAsync() do
849+
yield e.Current
850+
}
851+
783852
// Consider turning using an F# version of this instead?
784853
// https://github.com/i3arnon/ConcurrentHashSet
785854
type ConcurrentHashSet<'T when 'T: equality>(ct) =

0 commit comments

Comments
 (0)