From 56f4dbe1b88ea58843bb5252bea5f30aa55cf775 Mon Sep 17 00:00:00 2001 From: Abel Braaksma Date: Fri, 15 Mar 2024 16:36:41 +0100 Subject: [PATCH] Also simplify `skipWhile` and `skipWhileInclusive` (plus async variants) --- src/FSharp.Control.TaskSeq/TaskSeq.fs | 8 +-- src/FSharp.Control.TaskSeq/TaskSeqInternal.fs | 70 ++++++++----------- 2 files changed, 32 insertions(+), 46 deletions(-) diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs index 6fc2da8..520b3a3 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs @@ -303,10 +303,10 @@ type TaskSeq private () = 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 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 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 2ccabba..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 @@ -813,8 +806,10 @@ module internal TaskSeqInternal = | PredicateAsync asyncPredicate -> let mutable predicateHolds = true - while hasMore && predicateHolds do + + while hasMore && predicateHolds do // TODO: check perf if `while!` is going to be better or equal let! predicateIsTrue = asyncPredicate e.Current + if predicateIsTrue then yield e.Current let! cont = e.MoveNextAsync() @@ -828,51 +823,42 @@ module internal TaskSeqInternal = 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! notEmpty = e.MoveNextAsync() + let mutable hasMore = notEmpty - match! e.MoveNextAsync() with - | false -> () // Nothing further to do, no matter what the rules are - | true -> - - let exclusive = - match whileKind with - | Exclusive -> true - | Inclusive -> false + match predicate with + | Predicate synchronousPredicate -> + while hasMore && synchronousPredicate e.Current do + // keep skipping + let! cont = e.MoveNextAsync() + hasMore <- cont - let mutable cont = true + | PredicateAsync asyncPredicate -> + let mutable predicateHolds = true - match predicate with - | Predicate predicate -> // skipWhile(Inclusive)? - while cont do - if predicate e.Current then // spam -> skip - let! hasAnother = e.MoveNextAsync() - cont <- hasAnother - else // Starting the ham - if exclusive then - yield e.Current // return the item as it does not meet the condition for skipping + while hasMore && predicateHolds do // TODO: check perf if `while!` is going to be better or equal + let! predicateIsTrue = asyncPredicate e.Current - while! e.MoveNextAsync() do // propagate the rest - yield e.Current + if predicateIsTrue then + // keep skipping + let! cont = e.MoveNextAsync() + hasMore <- cont - cont <- false - | PredicateAsync predicate -> // skipWhile(Inclusive)?Async - while cont do - match! predicate e.Current with - | true -> - let! hasAnother = e.MoveNextAsync() - cont <- hasAnother - | false -> // We're starting the ham - if exclusive then - yield e.Current // return the item as it does not meet the condition for skipping + predicateHolds <- predicateIsTrue - while! e.MoveNextAsync() do // propagate the rest - yield e.Current + // "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 - cont <- false + // propagate the rest + while! e.MoveNextAsync() do + yield e.Current } // Consider turning using an F# version of this instead?