diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs index 7143446..520b3a3 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs @@ -299,14 +299,14 @@ type TaskSeq private () = static member take count source = Internal.skipOrTake Take count source static member truncate count source = Internal.skipOrTake Truncate count source - static member takeWhile predicate source = Internal.takeWhile Exclusive (Predicate predicate) source - static member takeWhileAsync predicate source = Internal.takeWhile Exclusive (PredicateAsync predicate) source - static member takeWhileInclusive predicate source = Internal.takeWhile Inclusive (Predicate predicate) source - static member takeWhileInclusiveAsync predicate source = Internal.takeWhile Inclusive (PredicateAsync predicate) source - static member skipWhile predicate source = Internal.skipWhile Exclusive (Predicate predicate) source - static member skipWhileAsync predicate source = Internal.skipWhile Exclusive (PredicateAsync predicate) source - static member skipWhileInclusive predicate source = Internal.skipWhile Inclusive (Predicate predicate) source - static member skipWhileInclusiveAsync predicate source = Internal.skipWhile Inclusive (PredicateAsync predicate) source + static member takeWhile predicate source = Internal.takeWhile false (Predicate predicate) source + static member takeWhileAsync predicate source = Internal.takeWhile false (PredicateAsync predicate) source + static member takeWhileInclusive predicate source = Internal.takeWhile true (Predicate predicate) source + static member takeWhileInclusiveAsync predicate source = Internal.takeWhile true (PredicateAsync predicate) source + static member skipWhile predicate source = Internal.skipWhile false (Predicate predicate) source + static member skipWhileAsync predicate source = Internal.skipWhile false (PredicateAsync predicate) source + static member skipWhileInclusive predicate source = Internal.skipWhile true (Predicate predicate) source + static member skipWhileInclusiveAsync predicate source = Internal.skipWhile true (PredicateAsync predicate) source static member tryPick chooser source = Internal.tryPick (TryPick chooser) source static member tryPickAsync chooser source = Internal.tryPick (TryPickAsync chooser) source diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index a062acd..c48821f 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -11,13 +11,6 @@ type internal AsyncEnumStatus = | WithCurrent | AfterAll -[] -type internal WhileKind = - /// The item under test is included (or skipped) even when the predicate returns false - | Inclusive - /// The item under test is always excluded (or not skipped) - | Exclusive - [] type internal TakeOrSkipKind = /// use the Seq.take semantics, raises exception if not enough elements @@ -796,140 +789,76 @@ module internal TaskSeqInternal = } - let takeWhile whileKind predicate (source: TaskSeq<_>) = + let takeWhile isInclusive predicate (source: TaskSeq<_>) = checkNonNull (nameof source) source taskSeq { use e = source.GetAsyncEnumerator CancellationToken.None let! notEmpty = e.MoveNextAsync() - let mutable more = notEmpty - - match whileKind, predicate with - | Exclusive, Predicate predicate -> // takeWhile - while more do - let value = e.Current - more <- predicate value - - if more then - // yield ONLY if predicate is true - yield value - let! hasMore = e.MoveNextAsync() - more <- hasMore + let mutable hasMore = notEmpty - | Inclusive, Predicate predicate -> // takeWhileInclusive - while more do - let value = e.Current - more <- predicate value - - // yield regardless of result of predicate - yield value - - if more then - let! hasMore = e.MoveNextAsync() - more <- hasMore + match predicate with + | Predicate synchronousPredicate -> + while hasMore && synchronousPredicate e.Current do + yield e.Current + let! cont = e.MoveNextAsync() + hasMore <- cont - | Exclusive, PredicateAsync predicate -> // takeWhileAsync - while more do - let value = e.Current - let! passed = predicate value - more <- passed + | PredicateAsync asyncPredicate -> + let mutable predicateHolds = true - if more then - // yield ONLY if predicate is true - yield value - let! hasMore = e.MoveNextAsync() - more <- hasMore + while hasMore && predicateHolds do // TODO: check perf if `while!` is going to be better or equal + let! predicateIsTrue = asyncPredicate e.Current - | Inclusive, PredicateAsync predicate -> // takeWhileInclusiveAsync - while more do - let value = e.Current - let! passed = predicate value - more <- passed + if predicateIsTrue then + yield e.Current + let! cont = e.MoveNextAsync() + hasMore <- cont - // yield regardless of predicate - yield value + predicateHolds <- predicateIsTrue - if more then - let! hasMore = e.MoveNextAsync() - more <- hasMore + // "inclusive" means: always return the item that we pulled, regardless of the result of applying the predicate + // and only stop thereafter. The non-inclusive versions, in contrast, do not return the item under which the predicate is false. + if hasMore && isInclusive then + yield e.Current } - let skipWhile whileKind predicate (source: TaskSeq<_>) = + let skipWhile isInclusive predicate (source: TaskSeq<_>) = checkNonNull (nameof source) source taskSeq { use e = source.GetAsyncEnumerator CancellationToken.None - let! moveFirst = e.MoveNextAsync() - let mutable more = moveFirst - - match whileKind, predicate with - | Exclusive, Predicate predicate -> // skipWhile - while more && predicate e.Current do - let! hasMore = e.MoveNextAsync() - more <- hasMore - - if more then - // yield the last one where the predicate was false - // (this ensures we skip 0 or more) - yield e.Current - - while! e.MoveNextAsync() do // get the rest - yield e.Current - - | Inclusive, Predicate predicate -> // skipWhileInclusive - while more && predicate e.Current do - let! hasMore = e.MoveNextAsync() - more <- hasMore - - if more then - // yield the rest (this ensures we skip 1 or more) - while! e.MoveNextAsync() do - yield e.Current - - | Exclusive, PredicateAsync predicate -> // skipWhileAsync - let mutable cont = true - - if more then - let! hasMore = predicate e.Current - cont <- hasMore - - while more && cont do - let! moveNext = e.MoveNextAsync() - - if moveNext then - let! hasMore = predicate e.Current - cont <- hasMore - - more <- moveNext - - if more then - // yield the last one where the predicate was false - // (this ensures we skip 0 or more) - yield e.Current + let! notEmpty = e.MoveNextAsync() + let mutable hasMore = notEmpty - while! e.MoveNextAsync() do // get the rest - yield e.Current + match predicate with + | Predicate synchronousPredicate -> + while hasMore && synchronousPredicate e.Current do + // keep skipping + let! cont = e.MoveNextAsync() + hasMore <- cont - | Inclusive, PredicateAsync predicate -> // skipWhileInclusiveAsync - let mutable cont = true + | PredicateAsync asyncPredicate -> + let mutable predicateHolds = true - if more then - let! hasMore = predicate e.Current - cont <- hasMore + while hasMore && predicateHolds do // TODO: check perf if `while!` is going to be better or equal + let! predicateIsTrue = asyncPredicate e.Current - while more && cont do - let! moveNext = e.MoveNextAsync() + if predicateIsTrue then + // keep skipping + let! cont = e.MoveNextAsync() + hasMore <- cont - if moveNext then - let! hasMore = predicate e.Current - cont <- hasMore + predicateHolds <- predicateIsTrue - more <- moveNext + // "inclusive" means: always skip the item that we pulled, regardless of the result of applying the predicate + // and only stop thereafter. The non-inclusive versions, in contrast, do not skip the item under which the predicate is false. + if hasMore && not isInclusive then + yield e.Current // don't skip, unless inclusive - if more then - // get the rest, this gives 1 or more semantics - while! e.MoveNextAsync() do - yield e.Current + // propagate the rest + while! e.MoveNextAsync() do + yield e.Current } // Consider turning using an F# version of this instead?