Skip to content

Commit

Permalink
Also simplify skipWhile and skipWhileInclusive (plus async variants)
Browse files Browse the repository at this point in the history
  • Loading branch information
abelbraaksma committed Mar 15, 2024
1 parent 4ed6ba5 commit 56f4dbe
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 46 deletions.
8 changes: 4 additions & 4 deletions src/FSharp.Control.TaskSeq/TaskSeq.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
70 changes: 28 additions & 42 deletions src/FSharp.Control.TaskSeq/TaskSeqInternal.fs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,6 @@ type internal AsyncEnumStatus =
| WithCurrent
| AfterAll

[<Struct>]
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

[<Struct>]
type internal TakeOrSkipKind =
/// use the Seq.take semantics, raises exception if not enough elements
Expand Down Expand Up @@ -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()
Expand All @@ -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?
Expand Down

0 comments on commit 56f4dbe

Please sign in to comment.