diff --git a/src/FSharp.Control.TaskSeq.sln.DotSettings b/src/FSharp.Control.TaskSeq.sln.DotSettings new file mode 100644 index 00000000..a8d97860 --- /dev/null +++ b/src/FSharp.Control.TaskSeq.sln.DotSettings @@ -0,0 +1,5 @@ + + True + True + True + True \ No newline at end of file diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fs b/src/FSharp.Control.TaskSeq/TaskSeq.fs index d3aedcb9..d1819e31 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fs @@ -314,15 +314,15 @@ type TaskSeq private () = static member exists predicate source = Internal.tryFind (Predicate predicate) source - |> Task.map (Option.isSome) + |> Task.map Option.isSome static member existsAsync predicate source = Internal.tryFind (PredicateAsync predicate) source - |> Task.map (Option.isSome) + |> Task.map Option.isSome static member contains value source = Internal.tryFind (Predicate((=) value)) source - |> Task.map (Option.isSome) + |> Task.map Option.isSome static member pick chooser source = Internal.tryPick (TryPick chooser) source @@ -348,8 +348,6 @@ type TaskSeq private () = Internal.tryFindIndex (PredicateAsync predicate) source |> Task.map (Option.defaultWith Internal.raiseNotFound) - - // // zip/unzip/fold etc functions // diff --git a/src/FSharp.Control.TaskSeq/TaskSeq.fsi b/src/FSharp.Control.TaskSeq/TaskSeq.fsi index c34895d2..e452dd0c 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeq.fsi +++ b/src/FSharp.Control.TaskSeq/TaskSeq.fsi @@ -81,7 +81,7 @@ type TaskSeq = /// /// Generates a new task sequence which, when iterated, will return successive elements by calling the given function - /// with the curren zero-basedt index, up to the given count. Each element is saved after its initialization for successive access to + /// with the current zero-based index, up to the given count. Each element is saved after its initialization for successive access to /// , which will not re-evaluate the . However, /// re-iterating the returned task sequence will re-evaluate the initialization function. The returned sequence may /// be passed between threads safely. However, individual IEnumerator values generated from the returned sequence should @@ -454,58 +454,58 @@ type TaskSeq = static member indexed: source: TaskSeq<'T> -> TaskSeq /// - /// Builds a new task sequence whose elements are the results of applying the + /// Builds a new task sequence whose elements are the results of applying the /// function to each of the elements of the input task sequence in . /// The given function will be applied as elements are pulled using the /// method on async enumerators retrieved from the input task sequence. /// Does not evaluate the input sequence until requested. /// /// - /// A function to transform items from the input task sequence. + /// A function to transform items from the input task sequence. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. static member map: mapper: ('T -> 'U) -> source: TaskSeq<'T> -> TaskSeq<'U> /// - /// Builds a new task sequence whose elements are the results of applying the + /// Builds a new task sequence whose elements are the results of applying the /// function to each of the elements of the input task sequence in , passing - /// an extra zero-based index argument to the function. + /// an extra zero-based index argument to the function. /// The given function will be applied as elements are pulled using the /// method on async enumerators retrieved from the input task sequence. /// Does not evaluate the input sequence until requested. /// /// - /// A function to transform items from the input task sequence that also access the current index. + /// A function to transform items from the input task sequence that also access the current index. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. static member mapi: mapper: (int -> 'T -> 'U) -> source: TaskSeq<'T> -> TaskSeq<'U> /// - /// Builds a new task sequence whose elements are the results of applying the asynchronous + /// Builds a new task sequence whose elements are the results of applying the asynchronous /// function to each of the elements of the input task sequence in . /// The given function will be applied as elements are pulled using the /// method on async enumerators retrieved from the input task sequence. /// Does not evaluate the input sequence until requested. /// /// - /// An asynchronous function to transform items from the input task sequence. + /// An asynchronous function to transform items from the input task sequence. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. static member mapAsync: mapper: ('T -> #Task<'U>) -> source: TaskSeq<'T> -> TaskSeq<'U> /// - /// Builds a new task sequence whose elements are the results of applying the asynchronous + /// Builds a new task sequence whose elements are the results of applying the asynchronous /// function to each of the elements of the input task sequence in , passing - /// an extra zero-based index argument to the function. + /// an extra zero-based index argument to the function. /// The given function will be applied as elements are pulled using the /// method on async enumerators retrieved from the input task sequence. /// Does not evaluate the input sequence until requested. /// /// - /// An asynchronous function to transform items from the input task sequence that also access the current index. + /// An asynchronous function to transform items from the input task sequence that also access the current index. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. @@ -640,6 +640,7 @@ type TaskSeq = /// /// /// The input task sequence. + /// The index of the item to retrieve. /// The nth element of the task sequence, or None if it doesn't exist. /// Thrown when the input task sequence is null. static member tryItem: index: int -> source: TaskSeq<'T> -> Task<'T option> @@ -651,6 +652,7 @@ type TaskSeq = /// /// /// The input task sequence. + /// The index of the item to retrieve. /// The nth element of the task sequence. /// Thrown when the input task sequence is null. /// Thrown when the sequence has insufficient length or is negative. @@ -818,7 +820,7 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, yields elements of the underlying sequence while the /// given function returns , and then returns no further elements. - /// The first element where the predicate returns is not included in the resulting sequence + /// Stops consuming the source and yielding items as soon as the predicate returns false. /// (see also ). /// If is asynchronous, consider using . /// @@ -832,7 +834,7 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, yields elements of the underlying sequence while the /// given asynchronous function returns , and then returns no further elements. - /// The first element where the predicate returns is not included in the resulting sequence + /// Stops consuming the source and yielding items as soon as the predicate returns false. /// (see also ). /// If is synchronous, consider using . /// @@ -874,8 +876,8 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, skips elements of the underlying sequence while the /// given function returns , and then yields the remaining - /// elements. The first element where the predicate returns is returned, which means that this - /// function will skip 0 or more elements (see also ). + /// elements. Elements where the predicate returns are propagated, which means that this + /// function may not skip any elements (see also ). /// If is asynchronous, consider using . /// /// @@ -888,8 +890,8 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, skips elements of the underlying sequence while the /// given asynchronous function returns , and then yields the - /// remaining elements. The first element where the predicate returns is returned, which - /// means that this function will skip 0 or more elements (see also ). + /// remaining elements. Elements where the predicate returns are propagated, which means that this + /// function may not skip any elements (see also ). /// If is synchronous, consider using . /// /// @@ -901,13 +903,13 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, skips elements of the underlying sequence until the given - /// function returns , also skips that element - /// and then yields the remaining elements (see also ). This function skips + /// function returns , also skips that element + /// and then yields the remaining elements (see also ). It will thus always skip /// at least one element of a non-empty sequence, or returns the empty task sequence if the input is empty. /// If is asynchronous, consider using . /// ` /// - /// A function that evaluates to false when no more items should be skipped. + /// A function that evaluates to false for the final item to be skipped. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. @@ -915,13 +917,13 @@ type TaskSeq = /// /// Returns a task sequence that, when iterated, skips elements of the underlying sequence until the given - /// function returns , also skips that element - /// and then yields the remaining elements (see also ). This function skips + /// function returns , also skips that element + /// and then yields the remaining elements (see also ). It will thus always skip /// at least one element of a non-empty sequence, or returns the empty task sequence if the input is empty. /// If is synchronous, consider using . /// /// - /// An asynchronous function that evaluates to false when no more items should be skipped. + /// An asynchronous function that evaluates to false for the final item to be skipped. /// The input task sequence. /// The resulting task sequence. /// Thrown when the input task sequence is null. @@ -1163,8 +1165,8 @@ type TaskSeq = /// /// Applies the function to each element in the task sequence, threading an accumulator - /// argument of type through the computation. If the input function is and the elements are - /// then computes . + /// argument of type through the computation. If the input function is f and the elements are i0...iN + /// then computes f (... (f s i0)...) iN. /// If the accumulator function is asynchronous, consider using . /// /// @@ -1177,8 +1179,8 @@ type TaskSeq = /// /// Applies the asynchronous function to each element in the task sequence, threading an accumulator - /// argument of type through the computation. If the input function is and the elements are - /// then computes . + /// argument of type through the computation. If the input function is f and the elements are i0...iN + /// then computes f (... (f s i0)...) iN. /// If the accumulator function is synchronous, consider using . /// /// diff --git a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs index e59d50c3..ed92a9fe 100644 --- a/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs +++ b/src/FSharp.Control.TaskSeq/TaskSeqInternal.fs @@ -163,7 +163,6 @@ module internal TaskSeqInternal = checkNonNull (nameof source) source task { - use e = source.GetAsyncEnumerator CancellationToken.None let mutable go = true let mutable i = 0 @@ -178,6 +177,77 @@ module internal TaskSeqInternal = return i } + let inline maxMin ([] maxOrMin) (source: TaskSeq<_>) = + checkNonNull (nameof source) source + + task { + use e = source.GetAsyncEnumerator CancellationToken.None + let! nonEmpty = e.MoveNextAsync() + + if not nonEmpty then + raiseEmptySeq () + + let mutable acc = e.Current + + while! e.MoveNextAsync() do + acc <- maxOrMin e.Current acc + + return acc + } + + // 'compare' is either `<` or `>` (i.e, less-than, greater-than resp.) + let inline maxMinBy ([] compare) ([] projection) (source: TaskSeq<_>) = + checkNonNull (nameof source) source + + task { + use e = source.GetAsyncEnumerator CancellationToken.None + let! nonEmpty = e.MoveNextAsync() + + if not nonEmpty then + raiseEmptySeq () + + let value = e.Current + let mutable accProjection = projection value + let mutable accValue = value + + while! e.MoveNextAsync() do + let value = e.Current + let currentProjection = projection value + + if compare accProjection currentProjection then + accProjection <- currentProjection + accValue <- value + + return accValue + } + + // 'compare' is either `<` or `>` (i.e, less-than, greater-than resp.) + let inline maxMinByAsync ([] compare) ([] projectionAsync) (source: TaskSeq<_>) = + checkNonNull (nameof source) source + + task { + use e = source.GetAsyncEnumerator CancellationToken.None + let! nonEmpty = e.MoveNextAsync() + + if not nonEmpty then + raiseEmptySeq () + + let value = e.Current + let! projValue = projectionAsync value + let mutable accProjection = projValue + let mutable accValue = value + + while! e.MoveNextAsync() do + let value = e.Current + let! currentProjection = projectionAsync value + + if compare accProjection currentProjection then + accProjection <- currentProjection + accValue <- value + + return accValue + } + let tryExactlyOne (source: TaskSeq<_>) = checkNonNull (nameof source) source