From 9c203e7cdcc56686b1ee03bc610809a387409eb6 Mon Sep 17 00:00:00 2001 From: Devin Lyons Date: Sun, 3 Nov 2024 19:27:04 -0900 Subject: [PATCH] Add NonEmptyList and NonEmptySeq Helper Functions (#610) * Add NonEmptyList helpers to match List. * Add NonEmptySeq helpers to match Seq. * Fix malformed tags in documentation. * Incorporate feedback. * Fix unit tests. * Adjust return types of try* functions. --- src/FSharpPlus/Data/NonEmptyList.fs | 805 +++++++++++++++++++++++++++- src/FSharpPlus/Data/NonEmptySeq.fs | 752 +++++++++++++++++++++++--- tests/FSharpPlus.Tests/Data.fs | 480 +++++++++++++++++ 3 files changed, 1925 insertions(+), 112 deletions(-) diff --git a/src/FSharpPlus/Data/NonEmptyList.fs b/src/FSharpPlus/Data/NonEmptyList.fs index 4747b6f66..20591524d 100644 --- a/src/FSharpPlus/Data/NonEmptyList.fs +++ b/src/FSharpPlus/Data/NonEmptyList.fs @@ -33,16 +33,22 @@ type nelist<'t> = NonEmptyList<'t> /// Basic operations on NonEmptyList [] module NonEmptyList = + /// Builds a non empty list. let create x xs = {Head = x; Tail = xs} + /// Builds a non empty list with a single element. let singleton x = {Head = x; Tail = []} + /// Builds a list from the given non empty list. let toList {Head = x; Tail = xs} = x::xs + /// Builds a sequence from the given non empty list. let toSeq {Head = x; Tail = xs} = seq { yield x; yield! xs; } + /// Builds an array from the given non empty list. let toArray nel = toList nel |> List.toArray + /// Builds a non empty list from the given array. /// The input array. /// Non empty list containing the elements of the array. @@ -52,6 +58,7 @@ module NonEmptyList = match array |> Array.toList with | [] -> invalidArg "array" "The input array was empty." | x::xs -> create x xs + /// Builds a non empty list from the given list. /// The input list. /// Non empty list containing the elements of the list. @@ -61,6 +68,7 @@ module NonEmptyList = match list with | [] -> invalidArg "list" "The input list was empty." | x::xs -> create x xs + /// Builds a non empty list from the given sequence. /// The input list. /// Non empty list containing the elements of the list. @@ -70,8 +78,35 @@ module NonEmptyList = match seq |> Seq.toList with | [] -> invalidArg "seq" "The input sequence was empty." | x::xs -> create x xs + + /// Attempts to build a non empty list from the given array. + /// The input array. + /// Non empty list containing the elements of the array. Returns None if no the input list is empty. + /// + let tryOfArray (array : _ array) = + match array |> Array.toList with + | [] -> None + | x::xs -> create x xs |> Some + + /// Attempts to build a non empty list from the given list. + /// The input list. + /// Non empty list containing the elements of the list. Returns None if no the input list is empty. + let tryOfList (list : _ list) = + match list with + | [] -> None + | x::xs -> create x xs |> Some + + /// Attempts to build a non empty list from the given sequence. + /// The input list. + /// Non empty list containing the elements of the list. Returns None if no the input list is empty. + let tryOfSeq (seq : _ seq) = + match seq |> Seq.toList with + | [] -> None + | x::xs -> create x xs |> Some + /// Returns the length of a non empty list. You can also use property nel.Length. let length (nel:_ NonEmptyList) = nel.Length + /// Build a new non empty list whose elements are the results of applying the given function /// to each of the elements of the non empty list. let map f {Head = x; Tail = xs} = {Head = f x; Tail = List.map f xs} @@ -113,15 +148,19 @@ module NonEmptyList = let zipShortest (list1: NonEmptyList<'T>) (list2: NonEmptyList<'U>) = { Head = (list1.Head, list2.Head); Tail = List.zipShortest list1.Tail list2.Tail } - /// Returns a new NonEmptyList with the element added to the beginning. let cons e {Head = x; Tail = xs} = {Head = e ; Tail = x::xs} + /// Returns the first element of a new non empty list. You can also use property nel.Head. let head {Head = x; Tail = _ } = x + /// Returns a new NonEmptyList of the elements trailing the first element. /// Thrown when the tail is empty. /// Throws exception for empty tail let tail {Head = _; Tail = xs } = ofList xs + + /// Returns a new NonEmptyList of the elements trailing the first element or None. + let tryTail {Head = _; Tail = xs } = tryOfList xs let rec tails s = let {Tail = xs} = s match xs with @@ -156,23 +195,452 @@ module NonEmptyList = #endif + /// Returns a new list that contains all pairings of elements from two lists. + /// The first input list. + /// The second input list. + /// The resulting list of pairs. + let inline allPairs (list1: NonEmptyList<'T>) (list2: NonEmptyList<'U>) = Seq.allPairs list1 list2 |> ofSeq + + /// Concatenates two lists. + /// The first input list. + /// The second input list. + /// The resulting list. + let inline append (list1: NonEmptyList<'T>) (list2: NonEmptyList<'T>) = + { Head = list1.Head; Tail = list1.Tail @ list2.Head :: list2.Tail } + /// Returns the average of the elements in the list. /// The input list. /// The resulting average. - let inline average (list: NonEmptyList<'T>) = List.average (list.Head :: list.Tail) + let inline average (list: NonEmptyList<'T>) = + Seq.average list /// Returns the average of the elements generated by applying the function to each element of the list. /// The function to transform the list elements into the type to be averaged. /// The input list. /// The resulting average. - let inline averageBy (projection: 'T -> ^U) list = List.averageBy projection (list.Head :: list.Tail) + let inline averageBy (projection: 'T -> ^U) (list: NonEmptyList<'T>) = + Seq.averageBy projection list + + /// + /// Applies a function to each element in a list and then returns a list of values v where the applied function returned Some(v). + /// + /// The function to be applied to the list elements. + /// The input list. + /// The resulting list comprising the values v where the chooser function returned Some(x). + let inline tryChoose chooser (list: NonEmptyList<'T>) = + list |> Seq.choose chooser |> tryOfSeq + + /// + /// Applies a function to each element in a list and then returns a list of values v where the applied function returned Some(v). + /// + /// The function to be applied to the list elements. + /// The input list. + /// The resulting list comprising the values v where the chooser function returned Some(x). + /// Thrown when the chooser function returns None for all elements. + let inline choose chooser (list: NonEmptyList<'T>) = + list |> Seq.choose chooser |> ofSeq + + /// Divides the input list into lists (chunks) of size at most chunkSize. + /// Returns a new list containing the generated lists (chunks) as its elements. + /// The maximum size of each chunk. + /// The input list. + /// The list divided into chunks. + let inline chunkBySize chunkSize (list: NonEmptyList<'T>): NonEmptyList> = + list.Head :: list.Tail |> List.chunkBySize chunkSize |> List.map ofList |> ofList + + /// For each element of the list, applies the given function. + /// Concatenates all the results and returns the combined list. + /// The function to transform each input element into a sublist to be concatenated. + /// The input list. + /// The concatenation of the transformed sublists. + let inline tryCollect mapping (list: NonEmptyList<'T>) = + list |> Seq.collect mapping |> tryOfSeq + + /// For each element of the list, applies the given function. + /// Concatenates all the results and returns the combined list. + /// The function to transform each input element into a sublist to be concatenated. + /// The input list. + /// The concatenation of the transformed sublists. + /// Thrown when the mapping function returns an empty list for all element. + let inline collect mapping (list: NonEmptyList<'T>) = + list >>= mapping + + /// Returns a new list that contains the elements of each of the lists in order. + /// The input list of lists. + /// The resulting concatenated list. + let inline concat (lists: NonEmptyList>) = + lists |> Seq.concat |> ofSeq + + /// Returns a new list that contains the elements of each of the lists in order. + /// Returns None if all of the inner lists are empty. + /// The input list of lists. + /// The resulting concatenated list or None. + let inline tryConcat (lists: NonEmptyList<#seq<'T>>) = + lists |> Seq.concat |> tryOfSeq + + /// Compares two lists using the given comparison function, element by element. + /// A function that takes an element from each list and returns an int. If it evaluates to a non-zero value iteration is stopped and that value is returned. + /// The first input list. + /// The second input list. + /// Returns the first non-zero result from the comparison function. + /// If the first list has a larger element, the return value is always positive. + /// If the second list has a larger element, the return value is always negative. + /// When the elements are equal in the two lists, 1 is returned if the first list is longer, 0 is returned if they are equal in length, and -1 is returned when the second list is longer. + /// + let inline compareWith comparer (list1: NonEmptyList<'T>) (list2: NonEmptyList<'T>) = + Seq.compareWith comparer list1 list2 + + /// Tests if the list contains the specified element. + /// The value to locate in the input list. + /// The input list. + /// True if the input list contains the specified element; false otherwise. + let inline contains (value: 'T) (list: NonEmptyList<'T>) = + Seq.contains value list + + /// Applies a key-generating function to each element of a list and returns a list yielding unique keys and their number of occurrences in the original list. + /// A function transforming each item of the input list into a key to be compared against the others. + /// The input list. + /// The resulting list of unique keys and their number of occurrences. + let inline countBy (projection: 'T -> 'U) (list: NonEmptyList<'T>) = + Seq.countBy projection list /// Returns a list that contains no duplicate entries according to the generic hash and equality comparisons /// on the keys returned by the given key-generating function. /// If an element occurs multiple times in the list then the later occurrences are discarded. /// The input list. /// The resulting list without duplicates. - let distinct (list: NonEmptyList<'T>) = list |> Seq.distinct |> ofSeq + let distinct (list: NonEmptyList<'T>) = + list |> Seq.distinct |> ofSeq + + /// Returns a list that contains no duplicate entries according to the generic hash and equality comparisons on the keys returned by the given key-generating function. + /// If an element occurs multiple times in the list then the later occurrences are discarded. + /// A function transforming the list items into comparable keys. + /// The input list. + /// The resulting list. + let inline distinctBy (projection: 'T -> 'U) (list: NonEmptyList<'T>) = + Seq.distinctBy projection list |> ofSeq + + /// Returns the only element of the list. + /// The input list. + /// The only element of the list. + /// Thrown when the input does not have precisely one element. + let inline exactlyOne (list: NonEmptyList<'T>) = + Seq.exactlyOne list + + /// Returns a new list with the distinct elements of the input list which do not appear in the itemsToExclude sequence, using generic hash and equality comparisons to compare values. + /// The sequence of items to exclude from the input list. + /// The input list. + /// A list that contains the distinct elements of list that do not appear in itemsToExclude. + /// Thrown when itemsToExclude is null. + let inline except (itemsToExclude: #seq<'T>) (list: NonEmptyList<'T>) = + Seq.except itemsToExclude list |> ofSeq + + /// Tests if any element of the list satisfies the given predicate. + /// The function to test the input elements. + /// The input list. + /// True if any element satisfies the predicate. + let inline exists (predicate: 'T -> bool) (list: NonEmptyList<'T>) = + Seq.exists predicate list + + /// Tests if any pair of corresponding elements of the lists satisfies the given predicate. + /// The function to test the input elements. + /// The first input list. + /// The second input list. + /// True if any pair of elements satisfy the predicate. + /// Thrown when the input lists are of different lengths. + let inline exists2 (predicate: 'T1 -> 'T2 -> bool) (list1: NonEmptyList<'T1>) (list2: NonEmptyList<'T2>) = + Seq.exists2 predicate list1 list2 + + /// Returns a new collection containing only the elements of the collection for which the given predicate returns "true." + /// The function to test the input elements. + /// The input list. + /// A list containing only the elements that satisfy the predicate. + let inline tryFilter (predicate: 'T -> bool) (list: NonEmptyList<'T>): NonEmptyList<'T> option = + list |> Seq.filter predicate |> tryOfSeq + + /// Returns a new collection containing only the elements of the collection for which the given predicate returns "true." + /// The function to test the input elements. + /// The input list. + /// A list containing only the elements that satisfy the predicate. + /// Thrown when the predicate evaluates to false for all the elements of the list. + let inline filter (predicate: 'T -> bool) (list: NonEmptyList<'T>): NonEmptyList<'T> = + list |> Seq.filter predicate |> ofSeq + + /// Returns the first element for which the given function returns True. + /// Raises if no such element exists. + /// The function to test the input elements. + /// The input list. + /// The first element that satisfies the predicate. + /// Thrown if the predicate evaluates to false for all the elements of the list. + let inline find (predicate: 'T -> bool) (list: NonEmptyList<'T>) = + Seq.find predicate list + + /// Returns the last element for which the given function returns True. + /// Raises if no such element exists. + /// The function to test the input elements. + /// The input list. + /// The first element that satisfies the predicate. + /// Thrown if the predicate evaluates to false for all the elements of the list. + let inline findBack (predicate: 'T -> bool) (list: NonEmptyList<'T>) = + Seq.findBack predicate list + + /// Returns the index of the first element in the list that satisfies the given predicate. + /// Raises if no such element exists. + /// The function to test the input elements. + /// The input list. + /// The first element that satisfies the predicate. + /// Thrown if the predicate evaluates to false for all the elements of the list. + let inline findIndex (predicate: 'T -> bool) (list: NonEmptyList<'T>) = + Seq.findIndex predicate list + + /// Returns the index of the last element in the list that satisfies the given predicate. + /// Raises if no such element exists. + /// The function to test the input elements. + /// The input list. + /// The first element that satisfies the predicate. + /// Thrown if the predicate evaluates to false for all the elements of the list. + let inline findIndexBack (predicate: 'T -> bool) (list: NonEmptyList<'T>) = + Seq.findIndexBack predicate list + + /// Applies a function to each element of the collection, threading an accumulator argument through the computation. + /// Take the second argument, and apply the function to it and the first element of the list. + /// Then feed this result into the function along with the second element and so on. + /// Return the final result. + /// If the input function is f and the elements are i0...iN then computes f (... (f s i0) i1 ...) iN. + /// The function to update the state given the input elements. + /// The initial state. + /// The input list. + /// The final state value. + let inline fold (folder: 'State -> 'T -> 'State) (state: 'State) (list: NonEmptyList<'T>) = + Seq.fold folder state list + + /// Applies a function to corresponding elements of two collections, threading an accumulator argument through the computation. + /// The collections must have identical sizes. + /// If the input function is f and the elements are i0...iN and j0...jN then computes f (... (f s i0 j0)...) iN jN. + /// The function to update the state given the input elements. + /// The initial state. + /// The first input list. + /// The second input list. + /// The final state value. + let inline fold2 (folder: 'State -> 'T1 -> 'T2 -> 'State) (state: 'State) (list1: NonEmptyList<'T1>) (list2: NonEmptyList<'T2>) = + Seq.fold2 folder state list1 list2 + + /// Applies a function to each element of the collection, starting from the end, threading an accumulator argument through the computation. + /// Take the second argument, and apply the function to it and the first element of the list. + /// Then feed this result into the function along with the second element and so on. + /// Return the final result. + /// If the input function is f and the elements are i0...iN then computes f i0 (...(f iN s)). + /// The function to update the state given the input elements. + /// The input list. + /// The initial state. + /// The final state value. + let inline foldBack (folder: 'T -> 'State -> 'State) (list: NonEmptyList<'T>) (state: 'State) = + Seq.foldBack folder list state + + /// Applies a function to corresponding elements of two collections, threading an accumulator argument through the computation. + /// The collections must have identical sizes. + /// If the input function is f and the elements are i0...iN and j0...jN then computes f (... (f s i0 j0)...) iN jN. + /// The function to update the state given the input elements. + /// The first input list. + /// The second input list. + /// The initial state. + /// The final state value. + let inline foldBack2 (folder: 'T1 -> 'T2 -> 'State -> 'State) (list1: NonEmptyList<'T1>) (list2: NonEmptyList<'T2>) (state: 'State) = + Seq.foldBack2 folder list1 list2 state + + /// Tests if all elements of the collection satisfy the given predicate. + /// The function to test the input elements. + /// The input list. + /// True if all of the elements satisfy the predicate. + let inline forall (predicate: 'T -> bool) (list: NonEmptyList<'T>) = + Seq.forall predicate list + + /// Tests if all corresponding elements of the collection satisfy the given predicate pairwise. + /// The function to test the input elements. + /// The first input list. + /// The second input list. + /// True if all of the pairs of elements satisfy the predicate. + /// Thrown when the input lists differ in length. + let inline forall2 (predicate: 'T1 -> 'T2 -> bool) (list1: NonEmptyList<'T1>) (list2: NonEmptyList<'T2>) = + Seq.forall2 predicate list1 list2 + + /// Applies a key-generating function to each element of a list and yields a list of unique keys. + /// Each unique key contains a list of all elements that match to this key. + /// A function that transforms an element of the list into a comparable key. + /// The input list. + /// The result list. + let inline groupBy (projection: 'T -> 'U) (list: NonEmptyList<'T>) = + Seq.groupBy projection list + |> Seq.map (fun (k, v) -> (k, ofSeq v)) + |> ofSeq + + /// Returns a list of each element in the list and its index. + /// The input list. + /// The result list. + let indexed (list: NonEmptyList<'T>) : NonEmptyList = + Seq.indexed list |> ofSeq + + /// Creates a list by applying a function to each index. + /// The number of elements to initialize. + /// A function that produces an element from an index. + /// The result list. + /// Thrown when count is less than or equal to zero. + let init (count: int) (initializer: int -> 'T) : NonEmptyList<'T> = + Seq.init count initializer |> ofSeq + + /// Inserts an element at the specified index. + /// The index at which to insert the element. + /// The value to insert. + /// The input list. + /// The result list. + let insertAt (index: int) (value: 'T) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.insertAt index value list |> ofSeq + + /// Inserts multiple elements at the specified index. + /// The index at which to insert the elements. + /// The values to insert. + /// The input list. + /// The result list. + let insertManyAt (index: int) (values: seq<'T>) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.insertManyAt index values list |> ofSeq + + /// Returns the element at the specified index. + /// The index of the element to retrieve. + /// The input list. + /// The element at the specified index. + /// Thrown when index is out of range. + let item (index: int) (list: NonEmptyList<'T>) : 'T = + Seq.item index list + + /// Applies a function to each element of the list. + /// The function to apply to each element. + /// The input list. + let iter (action: 'T -> unit) (list: NonEmptyList<'T>) : unit = + Seq.iter action list + + /// Applies a function to each element of the two lists. + /// The function to apply to each pair of elements. + /// The first input list. + /// The second input list. + let iter2 (action: 'T1 -> 'T2 -> unit) (list1: NonEmptyList<'T1>) (list2: NonEmptyList<'T2>) : unit = + Seq.iter2 action list1 list2 + + /// Applies a function to each element of the list, passing the index of the element as the first argument to the function. + /// The function to apply to each element and its index. + /// The input list. + let iteri (action: int -> 'T -> unit) (list: NonEmptyList<'T>) : unit = + Seq.iteri action list + + /// Applies a function to each element of the two lists, passing the index of the elements as the first argument to the function. + /// The function to apply to each pair of elements and their index. + /// The first input list. + /// The second input list. + let iteri2 (action: int -> 'T1 -> 'T2 -> unit) (list1: NonEmptyList<'T1>) (list2: NonEmptyList<'T2>) : unit = + Seq.iteri2 action list1 list2 + + /// Returns the last element of the list. + /// The input list. + /// The last element of the list. + let last (list: NonEmptyList<'T>) : 'T = + Seq.last list + + /// Applies a function to the corresponding elements of two lists, returning a list of the results. + /// The function to apply to each pair of elements. + /// The first input list. + /// The second input list. + /// The result list. + let map2 (mapping: 'T1 -> 'T2 -> 'U) (list1: NonEmptyList<'T1>) (list2: NonEmptyList<'T2>) : NonEmptyList<'U> = + Seq.map2 mapping list1 list2 |> ofSeq + + /// Applies a function to the corresponding elements of three lists, returning a list of the results. + /// The function to apply to each triplet of elements. + /// The first input list. + /// The second input list. + /// The third input list. + /// The result list. + let map3 (mapping: 'T1 -> 'T2 -> 'T3 -> 'U) (list1: NonEmptyList<'T1>) (list2: NonEmptyList<'T2>) (list3: NonEmptyList<'T3>) : NonEmptyList<'U> = + Seq.map3 mapping list1 list2 list3 |> ofSeq + + /// Applies a function to each element of the list, threading an accumulator argument through the computation. + /// The function to apply to each element and the accumulator. + /// The initial state. + /// The input list. + /// The result list and the final state. + let mapFold (mapping: 'State -> 'T -> 'Result * 'State) (state: 'State) (list: NonEmptyList<'T>) : NonEmptyList<'Result> * 'State = + let result, state = Seq.mapFold mapping state list + (ofSeq result, state) + + /// Applies a function to each element of the list, threading an accumulator argument through the computation. The function is applied to the elements of the list in reverse order. + /// The function to apply to each element and the accumulator. + /// The input list. + /// The initial state. + /// The result list and the final state. + let mapFoldBack (mapping: 'T -> 'State -> 'Result * 'State) (list: NonEmptyList<'T>) (state: 'State) : NonEmptyList<'Result> * 'State = + let result, state = Seq.mapFoldBack mapping list state + (ofSeq result, state) + + /// Applies a function to each element of the two lists, passing the index of the elements as the first argument to the function. + /// The function to apply to each pair of elements and their index. + /// The first input list. + /// The second input list. + /// The result list. + let mapi2 (mapping: int -> 'T1 -> 'T2 -> 'U) (list1: NonEmptyList<'T1>) (list2: NonEmptyList<'T2>) : NonEmptyList<'U> = + Seq.mapi2 mapping list1 list2 |> ofSeq + + /// Returns the greatest of all elements of the list, compared via Operators.max. + /// The input list. + /// The maximum element. + let max (list: NonEmptyList<'T>) = + List.max (list.Head :: list.Tail) + + /// Returns the greatest of all elements of the list, compared via Operators.max on the function result. + /// The function to transform the list elements into the type to be compared. + /// The input list. + /// The maximum element. + let maxBy (projection: 'T -> 'U) list = + List.maxBy projection (list.Head :: list.Tail) + + /// Returns the lowest of all elements of the list, compared via Operators.min. + /// The input list. + /// The minimum value. + let min (list: NonEmptyList<'T>) = + List.min (list.Head :: list.Tail) + + /// Returns the lowest of all elements of the list, compared via Operators.min on the function result + /// The function to transform list elements into the type to be compared. + /// The input list. + /// The minimum value. + let minBy (projection: 'T -> 'U) list = + List.minBy projection (list.Head :: list.Tail) + + /// Returns a list of each pair of consecutive elements in the list. + /// The input list. + /// The result list. + let pairwise (list: NonEmptyList<'T>) : NonEmptyList<'T * 'T> = + Seq.pairwise list |> ofSeq + + /// Splits the list into two lists, containing the elements for which the given function returns + /// true and false respectively. + /// A function to test each element of the list. + /// The input list. + /// A tuple containing the two lists. + let partition (predicate: 'T -> bool) (list: NonEmptyList<'T>) : 'T list * 'T list = + list |> toList |> List.partition predicate |> fun (a, b) -> (a, b) + + /// Applies a function to each element of the list, returning a list of the results in a random order. + /// A function to generate a permutation of the list indices. + /// The input list. + /// The result list. + let permute (permutation: int -> int) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.permute permutation list |> ofSeq + + /// Returns the first element for which the given function returns Some. If no such element exists, raises KeyNotFoundException. + /// A function to transform elements of the list into options. + /// The input list. + /// The first chosen element. + /// Thrown when no element is chosen. + let pick (chooser: 'T -> 'U option) (list: NonEmptyList<'T>) : 'U = + Seq.pick chooser list /// Applies a function to each element of the list, threading an accumulator argument /// through the computation. Apply the function to the first two elements of the list. @@ -182,7 +650,8 @@ module NonEmptyList = /// The function to reduce two list elements to a single element. /// The input list. /// The final reduced value. - let reduce (reduction: 'T -> 'T -> 'T) list = List.reduce reduction (list.Head :: list.Tail) + let reduce (reduction: 'T -> 'T -> 'T) (list: NonEmptyList<'T>) = + List.reduce reduction (list.Head :: list.Tail) /// Applies a function to each element of the list, starting from the end, threading an accumulator argument /// through the computation. If the input function is f and the elements are i0...iN then computes @@ -191,44 +660,322 @@ module NonEmptyList = /// current accumulated result to produce the next accumulated result. /// The input list. /// The final result of the reductions. - let reduceBack (reduction: 'T -> 'T -> 'T) list = List.reduceBack reduction (list.Head :: list.Tail) + let reduceBack (reduction: 'T -> 'T -> 'T) (list: NonEmptyList<'T>) = + List.reduceBack reduction (list.Head :: list.Tail) - /// Returns the greatest of all elements of the list, compared via Operators.max. - /// The input list. - /// The maximum element. - let max (list: NonEmptyList<'T>) = List.max (list.Head :: list.Tail) + /// Equivalent to [start..stop] on regular lists. + let inline range (start: 'T) stop = + create start (List.drop 1 [start..stop]) - /// Returns the greatest of all elements of the list, compared via Operators.max on the function result. - /// The function to transform the list elements into the type to be compared. + /// Removes the element at the specified index. + /// The index of the element to remove. /// The input list. - /// The maximum element. - let maxBy (projection: 'T -> 'U) list = List.maxBy projection (list.Head :: list.Tail) + /// The resulting list. + let tryRemoveAt (index: int) (list: NonEmptyList<'T>) : NonEmptyList<'T> option = + list |> Seq.removeAt index |> tryOfSeq - /// Returns the lowest of all elements of the list, compared via Operators.min. + /// Removes the element at the specified index. + /// The index of the element to remove. /// The input list. - /// The minimum value. - let min (list: NonEmptyList<'T>) = List.min (list.Head :: list.Tail) + /// The resulting list. + /// Thrown when removing the item results in an empty list. + let removeAt (index: int) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + list |> Seq.removeAt index |> ofSeq + + /// Removes multiple elements starting at the specified index. + /// The index at which to start removing elements. + /// The number of elements to remove. + /// The input list. + /// The result list. + let tryRemoveManyAt (index: int) (count: int) (list: NonEmptyList<'T>) : NonEmptyList<'T> option = + list |> Seq.removeManyAt index count |> tryOfSeq + + /// Removes multiple elements starting at the specified index. + /// The index at which to start removing elements. + /// The number of elements to remove. + /// The input list. + /// The result list. + /// Thrown when removing the items results in an empty list. + let removeManyAt (index: int) (count: int) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + list |> Seq.removeManyAt index count |> ofSeq + + /// Creates a list that contains one repeated value. + /// The number of elements. + /// The value to replicate. + /// The result list. + let replicate (count: int) (value: 'T) : NonEmptyList<'T> = + Seq.replicate count value |> ofSeq + + /// Reverses the elements of the list. + /// The input list. + /// The reversed list. + let rev (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.rev list |> ofSeq + + /// Applies a function to each element of the list, threading an accumulator argument through the computation. + /// A function that updates the state with each element. + /// The initial state. + /// The input list. + /// The list of state values. + let scan (folder: 'State -> 'T -> 'State) (state: 'State) (list: NonEmptyList<'T>) : NonEmptyList<'State> = + Seq.scan folder state list |> ofSeq + + /// Applies a function to each element of the list, threading an accumulator argument through the computation. The function is applied to the elements of the list in reverse order. + /// A function that updates the state with each element. + /// The initial state. + /// The input list. + /// The list of state values. + let scanBack (folder: 'T -> 'State -> 'State) (list: NonEmptyList<'T>) (state: 'State) : NonEmptyList<'State> = + Seq.scanBack folder list state |> ofSeq + + /// Returns a list that skips the first N elements of the list. + /// The number of elements to skip. + /// The input list. + /// The result list. + let trySkip (count: int) (list: NonEmptyList<'T>) : NonEmptyList<'T> option = + list |> Seq.skip count |> tryOfSeq + + /// Returns a list that skips the first N elements of the list. + /// The number of elements to skip. + /// The input list. + /// The result list. + /// Thrown when resulting list is empty. + let skip (count: int) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + list |> Seq.skip count |> ofSeq + + /// Returns a list that skips elements while the predicate is true. + /// A function to test each element of the list. + /// The input list. + /// The result list. + let trySkipWhile (predicate: 'T -> bool) (list: NonEmptyList<'T>) : NonEmptyList<'T> option = + list |> Seq.skipWhile predicate |> tryOfSeq + + /// Returns a list that skips elements while the predicate is true. + /// A function to test each element of the list. + /// The input list. + /// The result list. + /// Thrown when resulting list is empty. + let skipWhile (predicate: 'T -> bool) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + list |> Seq.skipWhile predicate |> ofSeq + + /// Sorts the elements of the list in ascending order. + /// The input list. + /// The sorted list. + let sort (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.sort list |> ofSeq + + /// Sorts the elements of the list in ascending order, using the given projection for comparison. + /// A function to transform the list elements before comparison. + /// The input list. + /// The sorted list. + let sortBy (projection: 'T -> 'Key) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.sortBy projection list |> ofSeq + + /// Sorts the elements of the list in descending order, using the given projection for comparison. + /// A function to transform the list elements before comparison. + /// The input list. + /// The sorted list. + let sortByDescending (projection: 'T -> 'Key) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.sortByDescending projection list |> ofSeq + + /// Sorts the elements of the list in descending order. + /// The input list. + /// The sorted list. + let sortDescending (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.sortDescending list |> ofSeq + + /// Sorts the elements of the list using the given comparison function. + /// A function to compare pairs of elements. + /// The input list. + /// The sorted list. + let sortWith (comparer: 'T -> 'T -> int) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.sortWith comparer list |> ofSeq + + /// Splits the list at the specified index. + /// The index at which to split the list. + /// The input list. + /// A tuple containing the two lists. + /// Thrown when the index is 0, equal to the size of the list, or is larger than the list. + let splitAt (index: int) (list: NonEmptyList<'T>) : NonEmptyList<'T> * NonEmptyList<'T> = + if index <= 0 then + raise <| new System.InvalidOperationException("Index must be greater than 0.") + else if index >= list.Length then + raise <| new System.InvalidOperationException("Index must be less than the length of the list.") + else + list |> toList |> List.splitAt index |> fun (a, b) -> (ofList a, ofList b) + + /// Splits the list into the specified number of lists. + /// The number of lists to create. + /// The input list. + /// A list of lists. + let splitInto (count: int) (list: NonEmptyList<'T>) : NonEmptyList> = + Seq.splitInto count list |> Seq.map ofSeq |> ofSeq + + /// Computes the sum of the elements of the list. + /// The input list. + /// The sum of the elements. + let inline sum list = + Seq.sum list + + /// Computes the sum of the elements of the list, using the given projection. + /// A function to transform the list elements before summing. + /// The input list. + /// The sum of the transformed elements. + let inline sumBy projection list = + Seq.sumBy projection list + + /// Returns a list that contains the first N elements of the list. + /// The number of elements to take. + /// The input list. + /// The result list. + let tryTake (count: int) (list: NonEmptyList<'T>) : NonEmptyList<'T> option = + Seq.take count list |> tryOfSeq + + /// Returns a list that contains the first N elements of the list. + /// The number of elements to take. + /// The input list. + /// The result list. + /// Thrown when the count is less than or equal to zero. + let take (count: int) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + if count <= 0 then + raise <| new System.ArgumentException("Count must be greater than 0.") + else + Seq.take count list |> ofSeq + + /// Returns a list that contains the elements of the list while the predicate is true. + /// A function to test each element of the list. + /// The input list. + /// The result list. + let tryTakeWhile (predicate: 'T -> bool) (list: NonEmptyList<'T>) : NonEmptyList<'T> option = + Seq.takeWhile predicate list |> tryOfSeq + + /// Returns a list that contains the elements of the list while the predicate is true. + /// A function to test each element of the list. + /// The input list. + /// The result list. + /// Thrown when resulting list is empty. + let takeWhile (predicate: 'T -> bool) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.takeWhile predicate list |> ofSeq + + /// Truncates the list to the specified length. + /// The maximum number of elements to include in the list. + /// The input list. + /// The truncated list. + let tryTruncate (count: int) (list: NonEmptyList<'T>) : NonEmptyList<'T> option = + Seq.truncate count list |> tryOfSeq + + /// Truncates the list to the specified length. + /// The maximum number of elements to include in the list. + /// The input list. + /// The truncated list. + let truncate (count: int) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + if count <= 0 then + raise <| new System.ArgumentException("Count must be greater than 0.") + else + Seq.truncate count list |> ofSeq + + /// Returns the only element of the list, or None if the list does not contain exactly one element. + /// The input list. + /// The only element of the list, or None. + let tryExactlyOne (list: NonEmptyList<'T>) : 'T option = + Seq.tryExactlyOne list + + /// Returns the first element for which the given function returns true, or None if no such element exists. + /// A function to test each element of the list. + /// The input list. + /// The first element for which the predicate returns true, or None. + let tryFind (predicate: 'T -> bool) (list: NonEmptyList<'T>) : 'T option = + Seq.tryFind predicate list + + /// Returns the last element for which the given function returns true, or None if no such element exists. + /// A function to test each element of the list. + /// The input list. + /// The last element for which the predicate returns true, or None. + let tryFindBack (predicate: 'T -> bool) (list: NonEmptyList<'T>) : 'T option = + Seq.tryFindBack predicate list + + /// Returns the index of the first element for which the given function returns true, or None if no such element exists. + /// A function to test each element of the list. + /// The input list. + /// The index of the first element for which the predicate returns true, or None. + let tryFindIndex (predicate: 'T -> bool) (list: NonEmptyList<'T>) : int option = + Seq.tryFindIndex predicate list + + /// Returns the index of the last element for which the given function returns true, or None if no such element exists. + /// A function to test each element of the list. + /// The input list. + /// The index of the last element for which the predicate returns true, or None. + let tryFindIndexBack (predicate: 'T -> bool) (list: NonEmptyList<'T>) : int option = + Seq.tryFindIndexBack predicate list - /// Returns the lowest of all elements of the list, compared via Operators.min on the function result - /// The function to transform list elements into the type to be compared. + /// Returns the element at the specified index, or None if the index is out of range. + /// The index of the element to retrieve. /// The input list. - /// The minimum value. - let minBy (projection: 'T -> 'U) list = List.minBy projection (list.Head :: list.Tail) + /// The element at the specified index, or None. + let tryItem (index: int) (list: NonEmptyList<'T>) : 'T option = + Seq.tryItem index list - /// Equivalent to [start..stop] on regular lists. - let inline range (start: 'T) stop = create start (List.drop 1 [start..stop]) + /// Returns the last element of the list, or None if the list is empty. + /// The input list. + /// The last element of the list, or None. + let tryLast (list: NonEmptyList<'T>) : 'T option = + Seq.tryLast list + + /// Returns the first element for which the given function returns Some, or None if no such element exists. + /// A function to transform elements of the list into options. + /// The input list. + /// The first chosen element, or None. + let tryPick (chooser: 'T -> 'U option) (list: NonEmptyList<'T>) : 'U option = + Seq.tryPick chooser list + + /// Generates a list by repeatedly applying a function to a state. + /// A function that takes the current state and returns an option tuple of the next element and the next state. + /// The initial state. + /// The result list. + let unfold (generator: 'State -> ('T * 'State) option) (state: 'State) : NonEmptyList<'T> = + Seq.unfold generator state |> ofSeq + + /// Splits a list of triples into three lists. + /// The input list. + /// A tuple containing the three lists. + let unzip3 (list: NonEmptyList<'T1 * 'T2 * 'T3>) : NonEmptyList<'T1> * NonEmptyList<'T2> * NonEmptyList<'T3> = + list |> toList |> List.unzip3 |> fun (a, b, c) -> (ofList a, ofList b, ofList c) + + /// Updates the element at the specified index. + /// The index of the element to update. + /// The new value. + /// The input list. + /// The result list. + let updateAt (index: int) (value: 'T) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.updateAt index value list |> ofSeq + + /// Returns a list that contains the elements of the list for which the given function returns true. + /// A function to test each element of the list. + /// The input list. + /// The result list. + let where (predicate: 'T -> bool) (list: NonEmptyList<'T>) : NonEmptyList<'T> = + Seq.where predicate list |> ofSeq + + /// Returns a list of sliding windows containing elements drawn from the list. + /// The number of elements in each window. + /// The input list. + /// The list of windows. + let windowed (windowSize: int) (list: NonEmptyList<'T>) : NonEmptyList> = + Seq.windowed windowSize list |> Seq.map ofSeq |> ofSeq + + /// Combines the elements of three lists into a list of triples. + /// The first input list. + /// The second input list. + /// The third input list. + /// The list of triples. + let zip3 (list1: NonEmptyList<'T1>) (list2: NonEmptyList<'T2>) (list3: NonEmptyList<'T3>) : NonEmptyList<'T1 * 'T2 * 'T3> = + Seq.zip3 list1 list2 list3 |> ofSeq #if !FABLE_COMPILER /// Reduces using alternative operator `<|>`. let inline choice (list: NonEmptyList<'``Alt<'T>``>) = reduce (<|>) list : '``Alt<'T>`` #endif - /// Transforms a list to a NonEmptyList, returning an option to signal when the original list was empty. - let tryOfList s = - match s with - | [] -> None - | x::xs -> Some (create x xs) - let ofNonEmptySeq (s: NonEmptySeq<_>) = create s.First (Seq.tail s |> List.ofSeq) diff --git a/src/FSharpPlus/Data/NonEmptySeq.fs b/src/FSharpPlus/Data/NonEmptySeq.fs index 3a29e8524..afea9d162 100644 --- a/src/FSharpPlus/Data/NonEmptySeq.fs +++ b/src/FSharpPlus/Data/NonEmptySeq.fs @@ -39,6 +39,52 @@ module NonEmptySeq = member _.First = x.[0] member _.GetEnumerator() = (x :> seq<_>).GetEnumerator() member _.GetEnumerator() = x.GetEnumerator() } + + /// Builds a non empty sequence from the given sequence. + /// The input sequence. + /// Non empty sequence containing the elements of the sequence. + /// Thrown when the input sequence is empty. + /// + /// Throws exception for empty sequence. + /// + /// Evaluates the first element of the sequence and may trigger side effects. + /// If you are sure that the sequence is not empty and want to avoid that, you can use `unsafeOfSeq` instead. + /// + /// + let ofSeq (seq: _ seq) = + if isNull seq || Seq.isEmpty seq then invalidArg "seq" "The input sequence was empty." + else unsafeOfSeq seq + + /// Transforms a sequence to a NonEmptySeq, returning an option to signal when the original sequence was empty. + let tryOfSeq (seq: _ seq) = + if isNull seq || Seq.isEmpty seq then None + else Some (unsafeOfSeq seq) + + /// Builds a non empty sequence from the given array. + /// The input array. + /// Non empty sequence containing the elements of the array. + /// Thrown when the input array is empty. + /// Throws exception for empty array + let ofArray (array: _[]) = + if isNull array || Array.isEmpty array then invalidArg "array" "The input array was empty." + else unsafeOfArray array + + /// Transforms a array to a NonEmptySeq, returning an option to signal when the original array was empty. + let tryOfArray (array: _[]) = + if isNull array || Array.isEmpty array then None + else Some (unsafeOfArray array) + + /// Builds a non empty sequence from the given list. + /// The input list. + /// Non empty sequence containing the elements of the list. + /// Thrown when the input list is empty. + /// Throws exception for empty list + let ofList (list: _ list) = + match list with [] -> invalidArg "list" "The input list was empty." | _ -> unsafeOfSeq list + + /// Transforms a list to a NonEmptySeq, returning an option to signal when the original list was empty. + let tryOfList (list: _ list) = + match list with [] -> None | _ -> unsafeOfSeq list |> Some /// Builds a non empty sequence. let create x xs = seq { yield x; yield! xs } |> unsafeOfSeq @@ -77,6 +123,18 @@ module NonEmptySeq = let appendSeqBack (seq: _ seq) (source: _ NonEmptySeq) = Seq.append seq source |> unsafeOfSeq + /// Returns the average of the elements in the list. + /// The input list. + /// The resulting average. + let inline average (list: NonEmptySeq<'T>) = Seq.average list + + /// Returns the average of the elements generated by applying the function to each element of the list. + /// The function to transform the list elements into the type to be averaged. + /// The input list. + /// The resulting average. + let inline averageBy (projection: 'T -> ^U) (list: NonEmptySeq<'T>) = + Seq.averageBy projection list + /// Returns a sequence that corresponds to a cached version of the input sequence. /// This result sequence will have the same elements as the input sequence. The result /// can be enumerated multiple times. The input sequence will be enumerated at most @@ -103,7 +161,40 @@ module NonEmptySeq = /// /// The result sequence. let cache (source: _ NonEmptySeq) = Seq.cache source |> unsafeOfSeq + + /// Wraps a loosely-typed System.Collections sequence as a typed sequence. + /// The input sequence. + /// The result sequence. + /// Thrown when the input sequence is null. + let cast (source: _ NonEmptySeq) = Seq.cast source |> unsafeOfSeq + + /// + /// Applies a function to each element in a sequence and then returns a sequence of values v where the applied function returned Some(v). + /// + /// The function to be applied to the list elements. + /// The input sequence. + /// The resulting sequence comprising the values v where the chooser function returned Some(x). + let inline tryChoose chooser (source: NonEmptySeq<'T>) = + source |> Seq.choose chooser |> tryOfSeq + /// + /// Applies a function to each element in a sequence and then returns a sequence of values v where the applied function returned Some(v). + /// + /// The function to be applied to the sequence elements. + /// The input sequence. + /// The resulting sequence comprising the values v where the chooser function returned Some(x). + /// Thrown when the chooser function returns None for all elements. + let inline choose chooser (source: NonEmptySeq<'T>) = + source |> Seq.choose chooser |> ofSeq + + /// Divides the input sequence into sequences (chunks) of size at most chunkSize. + /// Returns a new sequence containing the generated sequences (chunks) as its elements. + /// The maximum size of each chunk. + /// The input sequence. + /// The sequence divided into chunks. + let inline chunkBySize chunkSize (source: NonEmptySeq<'T>): NonEmptySeq> = + source |> Seq.chunkBySize chunkSize |> Seq.map unsafeOfSeq |> unsafeOfSeq + /// Applies the given function to each element of the sequence and concatenates all the /// results. /// @@ -116,6 +207,26 @@ module NonEmptySeq = /// The result sequence. let collect (mapping: 'a -> '``#NonEmptySeq<'b>``) (source: NonEmptySeq<'a>) : NonEmptySeq<'b> when '``#NonEmptySeq<'b>`` :> NonEmptySeq<'b> = Seq.collect mapping source |> unsafeOfSeq + + /// For each element of the list, applies the given function. + /// Concatenates all the results and returns the combined list. + /// The function to transform each input element into a sublist to be concatenated. + /// The input sequence. + /// The concatenation of the transformed sublists. + let tryCollect (mapping: 'a -> #seq<'b>) (source: NonEmptySeq<'a>) : NonEmptySeq<'b> option = + Seq.collect mapping source |> tryOfSeq + + /// Compares two sequences using the given comparison function, element by element. + /// A function that takes an element from each sequence and returns an int. If it evaluates to a non-zero value iteration is stopped and that value is returned. + /// The first input sequence. + /// The second input sequence. + /// Returns the first non-zero result from the comparison function. + /// If the first sequence has a larger element, the return value is always positive. + /// If the second sequence has a larger element, the return value is always negative. + /// When the elements are equal in the two sequences, 1 is returned if the first sequence is longer, 0 is returned if they are equal in length, and -1 is returned when the second list is longer. + /// + let compareWith comparer (source1: NonEmptySeq<'T>) (source2: NonEmptySeq<'T>) = + Seq.compareWith comparer source1 source2 /// Combines the given enumeration-of-enumerations as a single concatenated /// enumeration. @@ -129,6 +240,27 @@ module NonEmptySeq = let concat (sources: NonEmptySeq<'``#NonEmptySeq<'a>``>) : NonEmptySeq<'a> when '``#NonEmptySeq<'a>`` :> NonEmptySeq<'a> = Seq.concat sources |> unsafeOfSeq + /// Returns a new list that contains the elements of each of the lists in order. + /// Returns None if all of the inner lists are empty. + /// The input list of lists. + /// The resulting concatenated list or None. + let inline tryConcat (sources: NonEmptySeq<#seq<'T>>) = + sources |> Seq.concat |> tryOfSeq + + /// Tests if the sequence contains the specified element. + /// The value to locate in the input sequence. + /// The input sequence. + /// True if the input sequence contains the specified element; false otherwise. + let inline contains (value: 'T) (source: NonEmptySeq<'T>) = + Seq.contains value source + + /// Applies a key-generating function to each element of a sequence and returns a sequence yielding unique keys and their number of occurrences in the original list. + /// A function transforming each item of the input sequence into a key to be compared against the others. + /// The input sequence. + /// The resulting sequence of unique keys and their number of occurrences. + let inline countBy (projection: 'T -> 'U) (source: NonEmptySeq<'T>) = + Seq.countBy projection source + /// Returns a sequence that is built from the given delayed specification of a /// sequence. /// @@ -136,7 +268,177 @@ module NonEmptySeq = /// is requested. /// /// The generating function for the sequence. - let delay (generator: unit -> NonEmptySeq<'a>) : NonEmptySeq<'a> = Seq.delay (fun () -> generator () :> _) |> unsafeOfSeq + let delay (generator: unit -> NonEmptySeq<'a>) : NonEmptySeq<'a> = + Seq.delay (fun () -> generator () :> _) |> unsafeOfSeq + + /// Returns a sequence that contains no duplicate entries according to the generic hash and equality comparisons + /// on the keys returned by the given key-generating function. + /// If an element occurs multiple times in the sequence then the later occurrences are discarded. + /// The input sequence. + /// The resulting sequence without duplicates. + let distinct (source: NonEmptySeq<'T>) = + source |> Seq.distinct |> unsafeOfSeq + + /// Returns a sequence that contains no duplicate entries according to the generic hash and equality comparisons on the keys returned by the given key-generating function. + /// If an element occurs multiple times in the sequence then the later occurrences are discarded. + /// A function transforming the sequence items into comparable keys. + /// The input sequence. + /// The resulting sequence. + let inline distinctBy (projection: 'T -> 'U) (source: NonEmptySeq<'T>) = + Seq.distinctBy projection source |> unsafeOfSeq + + /// Returns the only element of the sequence. + /// The input sequence. + /// The only element of the sequence. + /// Thrown when the input does not have precisely one element. + let inline exactlyOne (source: NonEmptySeq<'T>) = + Seq.exactlyOne source + + /// Returns a new sequence with the distinct elements of the input sequence which do not appear in the itemsToExclude sequence, using generic hash and equality comparisons to compare values. + /// The sequence of items to exclude from the input sequence. + /// The input sequence. + /// A sequence that contains the distinct elements of sequence that do not appear in itemsToExclude. + /// Thrown when itemsToExclude is null. + let inline except (itemsToExclude: #seq<'T>) (source: NonEmptySeq<'T>) = + Seq.except itemsToExclude source |> ofSeq + + /// Tests if any element of the sequence satisfies the given predicate. + /// The function to test the input elements. + /// The input sequence. + /// True if any element satisfies the predicate. + let inline exists (predicate: 'T -> bool) (source: NonEmptySeq<'T>) = + Seq.exists predicate source + + /// Tests if any pair of corresponding elements of the sequences satisfies the given predicate. + /// The function to test the input elements. + /// The first input sequence. + /// The second input sequence. + /// True if any pair of elements satisfy the predicate. + /// Thrown when the input sequences are of different lengths. + let inline exists2 (predicate: 'T1 -> 'T2 -> bool) (source1: NonEmptySeq<'T1>) (source2: NonEmptySeq<'T2>) = + Seq.exists2 predicate source1 source2 + + /// Returns a new collection containing only the elements of the collection for which the given predicate returns "true." + /// The function to test the input elements. + /// The input sequence. + /// A sequence containing only the elements that satisfy the predicate. + let inline filter (predicate: 'T -> bool) (source: NonEmptySeq<'T>): NonEmptySeq<'T> = + source |> Seq.filter predicate |> ofSeq + + /// Returns a new collection containing only the elements of the collection for which the given predicate returns "true." + /// The function to test the input elements. + /// The input sequence. + /// A sequence containing only the elements that satisfy the predicate. + let inline tryFilter (predicate: 'T -> bool) (source: NonEmptySeq<'T>): NonEmptySeq<'T> option = + source |> Seq.filter predicate |> tryOfSeq + + /// Returns the first element for which the given function returns True. + /// Raises if no such element exists. + /// The function to test the input elements. + /// The input sequence. + /// The first element that satisfies the predicate. + /// Thrown if the predicate evaluates to false for all the elements of the sequence. + let inline find (predicate: 'T -> bool) (source: NonEmptySeq<'T>) = + Seq.find predicate source + + /// Returns the last element for which the given function returns True. + /// Raises if no such element exists. + /// The function to test the input elements. + /// The input sequence. + /// The first element that satisfies the predicate. + /// Thrown if the predicate evaluates to false for all the elements of the sequence. + let inline findBack (predicate: 'T -> bool) (source: NonEmptySeq<'T>) = + Seq.findBack predicate source + + /// Returns the index of the first element in the sequence that satisfies the given predicate. + /// Raises if no such element exists. + /// The function to test the input elements. + /// The input sequence. + /// The first element that satisfies the predicate. + /// Thrown if the predicate evaluates to false for all the elements of the sequence. + let inline findIndex (predicate: 'T -> bool) (source: NonEmptySeq<'T>) = + Seq.findIndex predicate source + + /// Returns the index of the last element in the sequence that satisfies the given predicate. + /// Raises if no such element exists. + /// The function to test the input elements. + /// The input sequence. + /// The first element that satisfies the predicate. + /// Thrown if the predicate evaluates to false for all the elements of the sequence. + let inline findIndexBack (predicate: 'T -> bool) (source: NonEmptySeq<'T>) = + Seq.findIndexBack predicate source + + /// Applies a function to each element of the collection, threading an accumulator argument through the computation. + /// Take the second argument, and apply the function to it and the first element of the sequence. + /// Then feed this result into the function along with the second element and so on. + /// Return the final result. + /// If the input function is f and the elements are i0...iN then computes f (... (f s i0) i1 ...) iN. + /// The function to update the state given the input elements. + /// The initial state. + /// The input sequence. + /// The final state value. + let inline fold (folder: 'State -> 'T -> 'State) (state: 'State) (source: NonEmptySeq<'T>) = + Seq.fold folder state source + + /// Applies a function to corresponding elements of two collections, threading an accumulator argument through the computation. + /// The collections must have identical sizes. + /// If the input function is f and the elements are i0...iN and j0...jN then computes f (... (f s i0 j0)...) iN jN. + /// The function to update the state given the input elements. + /// The initial state. + /// The first input sequence. + /// The second input sequence. + /// The final state value. + let inline fold2 (folder: 'State -> 'T1 -> 'T2 -> 'State) (state: 'State) (source1: NonEmptySeq<'T1>) (source2: NonEmptySeq<'T2>) = + Seq.fold2 folder state source1 source2 + + /// Applies a function to each element of the collection, starting from the end, threading an accumulator argument through the computation. + /// Take the second argument, and apply the function to it and the first element of the sequence. + /// Then feed this result into the function along with the second element and so on. + /// Return the final result. + /// If the input function is f and the elements are i0...iN then computes f i0 (...(f iN s)). + /// The function to update the state given the input elements. + /// The input sequence. + /// The initial state. + /// The final state value. + let inline foldBack (folder: 'T -> 'State -> 'State) (source: NonEmptySeq<'T>) (state: 'State) = + Seq.foldBack folder source state + + /// Applies a function to corresponding elements of two collections, threading an accumulator argument through the computation. + /// The collections must have identical sizes. + /// If the input function is f and the elements are i0...iN and j0...jN then computes f (... (f s i0 j0)...) iN jN. + /// The function to update the state given the input elements. + /// The first input sequence. + /// The second input sequence. + /// The initial state. + /// The final state value. + let inline foldBack2 (folder: 'T1 -> 'T2 -> 'State -> 'State) (source1: NonEmptySeq<'T1>) (source2: NonEmptySeq<'T2>) (state: 'State) = + Seq.foldBack2 folder source1 source2 state + + /// Tests if all elements of the collection satisfy the given predicate. + /// The function to test the input elements. + /// The input sequence. + /// True if all of the elements satisfy the predicate. + let inline forall (predicate: 'T -> bool) (source: NonEmptySeq<'T>) = + Seq.forall predicate source + + /// Tests if all corresponding elements of the collection satisfy the given predicate pairwise. + /// The function to test the input elements. + /// The first input sequence. + /// The second input sequence. + /// True if all of the pairs of elements satisfy the predicate. + /// Thrown when the input sequences differ in length. + let inline forall2 (predicate: 'T1 -> 'T2 -> bool) (source1: NonEmptySeq<'T1>) (source2: NonEmptySeq<'T2>) = + Seq.forall2 predicate source1 source2 + + /// Applies a key-generating function to each element of a sequence and yields a sequence of unique keys. + /// Each unique key contains a sequence of all elements that match to this key. + /// A function that transforms an element of the sequence into a comparable key. + /// The input sequence. + /// The result sequence. + let inline groupBy (projection: 'T -> 'U) (source: NonEmptySeq<'T>) = + Seq.groupBy projection source + |> Seq.map (fun (k, v) -> (k, unsafeOfSeq v)) + |> unsafeOfSeq /// Returns the first element of the sequence. /// @@ -149,7 +451,16 @@ module NonEmptySeq = /// paired with the integer index (from 0) of each element. /// The input sequence. /// The result sequence. - let indexed (source: NonEmptySeq<_>) = Seq.indexed source |> unsafeOfSeq + let indexed (source: NonEmptySeq<_>) = + Seq.indexed source |> unsafeOfSeq + + /// Creates a sequence by applying a function to each index. + /// The number of elements to initialize. + /// A function that produces an element from an index. + /// The result sequence. + /// Thrown when count is less than or equal to zero. + let init (count: int) (initializer: int -> 'T) : NonEmptySeq<'T> = + Seq.init count initializer |> unsafeOfSeq /// Generates a new sequence which, when iterated, will return successive /// elements by calling the given function. The results of calling the function @@ -164,7 +475,70 @@ module NonEmptySeq = /// A function that generates an item in the sequence from a given index. /// /// The result sequence. - let initInfinite initializer = Seq.initInfinite initializer |> unsafeOfSeq + let initInfinite initializer = + Seq.initInfinite initializer |> unsafeOfSeq + + /// Inserts an element at the specified index. + /// The index at which to insert the element. + /// The value to insert. + /// The input sequence. + /// The result sequence. + let insertAt (index: int) (value: 'T) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = + Seq.insertAt index value source |> unsafeOfSeq + + /// Inserts multiple elements at the specified index. + /// The index at which to insert the elements. + /// The values to insert. + /// The input sequence. + /// The result sequence. + let insertManyAt (index: int) (values: seq<'T>) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = + Seq.insertManyAt index values source |> unsafeOfSeq + + /// Returns the element at the specified index. + /// The index of the element to retrieve. + /// The input sequence. + /// The element at the specified index. + /// Thrown when index is out of range. + let item (index: int) (source: NonEmptySeq<'T>) : 'T = + Seq.item index source + + /// Applies a function to each element of the sequence. + /// The function to apply to each element. + /// The input sequence. + let iter (action: 'T -> unit) (source: NonEmptySeq<'T>) : unit = + Seq.iter action source + + /// Applies a function to each element of the two sequences. + /// The function to apply to each pair of elements. + /// The first input sequence. + /// The second input sequence. + let iter2 (action: 'T1 -> 'T2 -> unit) (source1: NonEmptySeq<'T1>) (source2: NonEmptySeq<'T2>) : unit = + Seq.iter2 action source1 source2 + + /// Applies a function to each element of the sequence, passing the index of the element as the first argument to the function. + /// The function to apply to each element and its index. + /// The input sequence. + let iteri (action: int -> 'T -> unit) (source: NonEmptySeq<'T>) : unit = + Seq.iteri action source + + /// Applies a function to each element of the two sequences, passing the index of the elements as the first argument to the function. + /// The function to apply to each pair of elements and their index. + /// The first input sequence. + /// The second input sequence. + let iteri2 (action: int -> 'T1 -> 'T2 -> unit) (source1: NonEmptySeq<'T1>) (source2: NonEmptySeq<'T2>) : unit = + Seq.iteri2 action source1 source2 + + /// Returns the last element of the sequence. + /// The input sequence. + /// The last element of the sequence. + let last (source: NonEmptySeq<'T>) : 'T = + Seq.last source + + /// Returns the last element of the sequence. + /// The input sequence. + /// The last element of the sequence. + let length (source: NonEmptySeq<'T>) : int = + Seq.length source /// Builds a new collection whose elements are the results of applying the given function /// to each of the elements of the collection. The given function will be applied @@ -178,7 +552,8 @@ module NonEmptySeq = /// The input sequence. /// /// The result sequence. - let map mapping (source: NonEmptySeq<_>) = source |> Seq.map mapping |> unsafeOfSeq + let map mapping (source: NonEmptySeq<_>) = + source |> Seq.map mapping |> unsafeOfSeq /// Builds a new collection whose elements are the results of applying the given function /// to the corresponding pairs of elements from the two sequences. If one input sequence is shorter than @@ -189,7 +564,8 @@ module NonEmptySeq = /// The second input sequence. /// /// The result sequence. - let map2 mapping (source1: NonEmptySeq<_>) (source2: NonEmptySeq<_>) = Seq.map2 mapping source1 source2 |> unsafeOfSeq + let map2 mapping (source1: NonEmptySeq<_>) (source2: NonEmptySeq<_>) = + Seq.map2 mapping source1 source2 |> unsafeOfSeq /// Combines map and fold. Builds a new collection whose elements are the results of applying the given function /// to each of the elements of the collection. The function is also used to accumulate a final value. @@ -238,7 +614,8 @@ module NonEmptySeq = /// The input sequence. /// /// The result sequence. - let mapi mapping (source: NonEmptySeq<_>) = Seq.mapi mapping source |> unsafeOfSeq + let mapi mapping (source: NonEmptySeq<_>) = + Seq.mapi mapping source |> unsafeOfSeq /// Builds a new collection whose elements are the results of applying the given function /// to the corresponding pairs of elements from the two sequences. If one input sequence is shorter than @@ -250,53 +627,8 @@ module NonEmptySeq = /// The second input sequence. /// /// The result sequence. - let mapi2 mapping (source1: NonEmptySeq<_>) (source2: NonEmptySeq<_>) = Seq.mapi2 mapping source1 source2 |> unsafeOfSeq - - /// Builds a non empty sequence from the given sequence. - /// The input sequence. - /// Non empty sequence containing the elements of the sequence. - /// Thrown when the input sequence is empty. - /// - /// Throws exception for empty sequence. - /// - /// Evaluates the first element of the sequence and may trigger side effects. - /// If you are sure that the sequence is not empty and want to avoid that, you can use `unsafeOfSeq` instead. - /// - /// - let ofSeq (seq: _ seq) = - if isNull seq || Seq.isEmpty seq then invalidArg "seq" "The input sequence was empty." - else unsafeOfSeq seq - - /// Transforms a sequence to a NonEmptySeq, returning an option to signal when the original sequence was empty. - let tryOfSeq (seq: _ seq) = - if isNull seq || Seq.isEmpty seq then None - else Some (unsafeOfSeq seq) - - /// Builds a non empty sequence from the given array. - /// The input array. - /// Non empty sequence containing the elements of the array. - /// Thrown when the input array is empty. - /// Throws exception for empty array - let ofArray (array: _[]) = - if isNull array || Array.isEmpty array then invalidArg "array" "The input array was empty." - else unsafeOfArray array - - /// Transforms a array to a NonEmptySeq, returning an option to signal when the original array was empty. - let tryOfArray (array: _[]) = - if isNull array || Array.isEmpty array then None - else Some (unsafeOfArray array) - - /// Builds a non empty sequence from the given list. - /// The input list. - /// Non empty sequence containing the elements of the list. - /// Thrown when the input list is empty. - /// Throws exception for empty list - let ofList (list: _ list) = - match list with [] -> invalidArg "list" "The input list was empty." | _ -> unsafeOfSeq list - - /// Transforms a list to a NonEmptySeq, returning an option to signal when the original list was empty. - let tryOfList (list: _ list) = - match list with [] -> None | _ -> unsafeOfSeq list |> Some + let mapi2 mapping (source1: NonEmptySeq<_>) (source2: NonEmptySeq<_>) = + Seq.mapi2 mapping source1 source2 |> unsafeOfSeq /// Returns a sequence of each element in the input sequence and its predecessor, with the /// exception of the first element which is only returned as the predecessor of the second element. @@ -304,7 +636,8 @@ module NonEmptySeq = /// The input sequence. /// /// The result sequence. - let pairwise (source: NonEmptySeq<_>) = Seq.pairwise source |> unsafeOfSeq + let pairwise (source: NonEmptySeq<_>) = + Seq.pairwise source |> unsafeOfSeq /// Returns a sequence with all elements permuted according to the /// specified permutation. @@ -320,7 +653,16 @@ module NonEmptySeq = /// /// Thrown when indexMap does not produce a valid permutation. /// This function consumes the whole input sequence before yielding the first element of the result sequence. - let permute indexMap (source: NonEmptySeq<_>) = Seq.permute indexMap source |> unsafeOfSeq + let permute indexMap (source: NonEmptySeq<_>) = + Seq.permute indexMap source |> unsafeOfSeq + + /// Returns the first element for which the given function returns Some. If no such element exists, raises KeyNotFoundException. + /// A function to transform elements of the sequence into options. + /// The input sequence. + /// The first chosen element. + /// Thrown when no element is chosen. + let pick (chooser: 'T -> 'U option) (source: NonEmptySeq<'T>) : 'U = + Seq.pick chooser source /// Builds a new sequence object that delegates to the given sequence object. This ensures /// the original sequence cannot be rediscovered and mutated by a type cast. For example, @@ -330,13 +672,75 @@ module NonEmptySeq = /// The input sequence. /// /// The result sequence. - let readonly (source: NonEmptySeq<_>) = Seq.readonly source |> unsafeOfSeq + let readonly (source: NonEmptySeq<_>) = + Seq.readonly source |> unsafeOfSeq + + /// Applies a function to each element of the sequence, threading an accumulator argument + /// through the computation. Apply the function to the first two elements of the sequence. + /// Then feed this result into the function along with the third element and so on. + /// Return the final result. If the input function is f and the elements are i0...iN then computes + /// f (... (f i0 i1) i2 ...) iN. + /// The function to reduce two sequence elements to a single element. + /// The input sequence. + /// The final reduced value. + let reduce (reduction: 'T -> 'T -> 'T) source = + Seq.reduce reduction source + + /// Applies a function to each element of the sequence, starting from the end, threading an accumulator argument + /// through the computation. If the input function is f and the elements are i0...iN then computes + /// f i0 (...(f iN-1 iN)). + /// A function that takes in the next-to-last element of the sequence and the + /// current accumulated result to produce the next accumulated result. + /// The input sequence. + /// The final result of the reductions. + let reduceBack (reduction: 'T -> 'T -> 'T) (source: NonEmptySeq<'T>) = + Seq.reduceBack reduction source + + /// Removes the element at the specified index. + /// The index of the element to remove. + /// The input sequence. + /// The result sequence. + let tryRemoveAt (index: int) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> option = + Seq.removeAt index source |> tryOfSeq + + /// Removes the element at the specified index. + /// The index of the element to remove. + /// The input sequence. + /// The result sequence. + /// Thrown when removing the item results in an empty sequence. + let removeAt (index: int) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = + Seq.removeAt index source |> ofSeq + + /// Removes multiple elements starting at the specified index. + /// The index at which to start removing elements. + /// The number of elements to remove. + /// The input sequence. + /// The result sequence. + let tryRemoveManyAt (index: int) (count: int) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> option = + Seq.removeManyAt index count source |> tryOfSeq + + /// Removes multiple elements starting at the specified index. + /// The index at which to start removing elements. + /// The number of elements to remove. + /// The input sequence. + /// The result sequence. + /// Thrown when removing the items results in an empty sequence. + let removeManyAt (index: int) (count: int) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = + Seq.removeManyAt index count source |> ofSeq + + /// Creates a sequence that contains one repeated value. + /// The number of elements. + /// The value to replicate. + /// The result sequence. + let replicate (count: int) (value: 'T) : NonEmptySeq<'T> = + Seq.replicate count value |> unsafeOfSeq /// Returns a new sequence with the elements in reverse order. /// The input sequence. /// The reversed sequence. /// This function consumes the whole input sequence before yielding the first element of the reversed sequence. - let rev (source: NonEmptySeq<_>) = Seq.rev source |> unsafeOfSeq + let rev (source: NonEmptySeq<_>) = + Seq.rev source |> unsafeOfSeq /// Like fold, but computes on-demand and returns the sequence of intermediary and final results. /// @@ -365,7 +769,38 @@ module NonEmptySeq = /// The input item. /// /// The result sequence of one item. - let singleton value = Seq.singleton value |> unsafeOfSeq + let singleton value = + Seq.singleton value |> unsafeOfSeq + + /// Returns a sequence that skips the first N elements of the list. + /// The number of elements to skip. + /// The input sequence. + /// The result sequence. + let trySkip (count: int) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> option = + Seq.skip count source |> tryOfSeq + + /// Returns a sequence that skips the first N elements of the list. + /// The number of elements to skip. + /// The input sequence. + /// The result sequence. + /// Thrown when resulting list is empty. + let skip (count: int) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = + Seq.skip count source |> unsafeOfSeq + + /// Returns a sequence that skips elements while the predicate is true. + /// A function to test each element of the sequence. + /// The input sequence. + /// The result sequence. + let trySkipWhile (predicate: 'T -> bool) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> option = + Seq.skipWhile predicate source |> tryOfSeq + + /// Returns a sequence that skips elements while the predicate is true. + /// A function to test each element of the sequence. + /// The input sequence. + /// The result sequence. + /// Thrown when resulting list is empty. + let skipWhile (predicate: 'T -> bool) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = + Seq.skipWhile predicate source |> unsafeOfSeq /// Yields a sequence ordered by keys. /// @@ -381,7 +816,8 @@ module NonEmptySeq = /// The result sequence. /// /// This function consumes the whole input sequence before yielding the first element of the result sequence. - let sort (source: NonEmptySeq<_>) = Seq.sort source |> unsafeOfSeq + let sort (source: NonEmptySeq<_>) = + Seq.sort source |> unsafeOfSeq /// Yields a sequence ordered using the given comparison function. /// This function returns a sequence that digests the whole initial sequence as soon as @@ -394,7 +830,8 @@ module NonEmptySeq = /// The input sequence. /// The result sequence. /// This function consumes the whole input sequence before yielding the first element of the result sequence. - let sortWith comparer (source: NonEmptySeq<_>) = Seq.sortWith comparer source |> unsafeOfSeq + let sortWith comparer (source: NonEmptySeq<_>) = + Seq.sortWith comparer source |> unsafeOfSeq /// Applies a key-generating function to each element of a sequence and yield a sequence ordered /// by keys. The keys are compared using generic comparison as implemented by Operators.compare. @@ -412,7 +849,8 @@ module NonEmptySeq = /// The result sequence. /// /// Thrown when the input sequence is null. - let sortBy projection (source: NonEmptySeq<_>) = Seq.sortBy projection source |> unsafeOfSeq + let sortBy projection (source: NonEmptySeq<_>) = + Seq.sortBy projection source |> unsafeOfSeq /// Yields a sequence ordered descending by keys. /// @@ -426,7 +864,8 @@ module NonEmptySeq = /// The input sequence. /// /// The result sequence. - let sortDescending (source: NonEmptySeq<_>) = Seq.sortDescending source |> unsafeOfSeq + let sortDescending (source: NonEmptySeq<_>) = + Seq.sortDescending source |> unsafeOfSeq /// Applies a key-generating function to each element of a sequence and yield a sequence ordered /// descending by keys. The keys are compared using generic comparison as implemented by Operators.compare. @@ -442,7 +881,28 @@ module NonEmptySeq = /// The input sequence. /// /// The result sequence. - let sortByDescending projection (source: NonEmptySeq<_>) = Seq.sortByDescending projection source |> unsafeOfSeq + let sortByDescending projection (source: NonEmptySeq<_>) = + Seq.sortByDescending projection source |> unsafeOfSeq + + /// Splits the list into the specified number of sequences. + /// The number of sequences to create. + /// The input list. + /// A sequence of sequences. + let splitInto (count: int) (source: NonEmptySeq<'T>) : NonEmptySeq> = + Seq.splitInto count source |> Seq.map unsafeOfSeq |> unsafeOfSeq + + /// Computes the sum of the elements of the sequence. + /// The input sequence. + /// The sum of the elements. + let inline sum source = + Seq.sum source + + /// Computes the sum of the elements of the sequence, using the given projection. + /// A function to transform the sequence elements before summing. + /// The input sequence. + /// The sum of the transformed elements. + let inline sumBy projection source = + Seq.sumBy projection source /// Returns a sequence that skips 1 element of the underlying sequence and then yields the /// remaining elements of the sequence. @@ -450,8 +910,121 @@ module NonEmptySeq = /// The input sequence. /// /// The result sequence. - let tail (source: NonEmptySeq<_>) = Seq.tail source + let tail (source: NonEmptySeq<_>) = + Seq.tail source + + /// Returns a sequence that skips 1 element of the underlying sequence and then yields the + /// remaining elements of the sequence. + /// + /// The input sequence. + /// + /// The result sequence. + let tryTail (source: NonEmptySeq<_>) = + Seq.tail source |> tryOfSeq + + /// Returns a sequence that contains the first N elements of the sequence. + /// The number of elements to take. + /// The input sequence. + /// The result sequence. + let tryTake (count: int) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> option = + Seq.take count source |> tryOfSeq + + /// Returns a sequence that contains the first N elements of the sequence. + /// The number of elements to take. + /// The input sequence. + /// The result sequence. + /// Thrown when the count is less than or equal to zero. + let take (count: int) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = + if count <= 0 then + raise <| new System.ArgumentException("Count must be greater than 0.") + else + Seq.take count source |> unsafeOfSeq + + /// Returns a sequence that contains the elements of the sequence while the predicate is true. + /// A function to test each element of the sequence. + /// The input sequence. + /// The result sequence. + let tryTakeWhile (predicate: 'T -> bool) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> option = + Seq.takeWhile predicate source |> tryOfSeq + + /// Returns a sequence that contains the elements of the sequence while the predicate is true. + /// A function to test each element of the sequence. + /// The input sequence. + /// The result sequence. + /// Thrown when resulting sequence is empty. + let takeWhile (predicate: 'T -> bool) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = + Seq.takeWhile predicate source |> unsafeOfSeq + + /// Truncates the sequence to the specified length. + /// The maximum number of elements to include in the sequence. + /// The input sequence. + /// The truncated sequence. + /// Thrown when the count is less than or equal to zero. + let truncate (count: int) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = + if count <= 0 then + raise <| new System.ArgumentException("Count must be greater than 0.") + else + Seq.truncate count source |> unsafeOfSeq + + /// Returns the only element of the sequence, or None if the sequence does not contain exactly one element. + /// The input sequence. + /// The only element of the sequence, or None. + let tryExactlyOne (source: NonEmptySeq<'T>) : 'T option = + Seq.tryExactlyOne source + + /// Returns the first element for which the given function returns true, or None if no such element exists. + /// A function to test each element of the sequence. + /// The input sequence. + /// The first element for which the predicate returns true, or None. + let tryFind (predicate: 'T -> bool) (source: NonEmptySeq<'T>) : 'T option = + Seq.tryFind predicate source + + /// Returns the last element for which the given function returns true, or None if no such element exists. + /// A function to test each element of the sequence. + /// The input sequence. + /// The last element for which the predicate returns true, or None. + let tryFindBack (predicate: 'T -> bool) (source: NonEmptySeq<'T>) : 'T option = + Seq.tryFindBack predicate source + + /// Returns the index of the first element for which the given function returns true, or None if no such element exists. + /// A function to test each element of the sequence. + /// The input sequence. + /// The index of the first element for which the predicate returns true, or None. + let tryFindIndex (predicate: 'T -> bool) (source: NonEmptySeq<'T>) : int option = + Seq.tryFindIndex predicate source + + /// Returns the index of the last element for which the given function returns true, or None if no such element exists. + /// A function to test each element of the sequence. + /// The input sequence. + /// The index of the last element for which the predicate returns true, or None. + let tryFindIndexBack (predicate: 'T -> bool) (source: NonEmptySeq<'T>) : int option = + Seq.tryFindIndexBack predicate source + /// Returns the element at the specified index, or None if the index is out of range. + /// The index of the element to retrieve. + /// The input sequence. + /// The element at the specified index, or None. + let tryItem (index: int) (source: NonEmptySeq<'T>) : 'T option = + Seq.tryItem index source + + /// Returns the last element of the sequence, or None if the sequence is empty. + /// The input sequence. + /// The last element of the sequence, or None. + let tryLast (source: NonEmptySeq<'T>) : 'T option = + Seq.tryLast source + + /// Returns the first element for which the given function returns Some, or None if no such element exists. + /// A function to transform elements of the sequence into options. + /// The input sequence. + /// The first chosen element, or None. + let tryPick (chooser: 'T -> 'U option) (source: NonEmptySeq<'T>) : 'U option = + Seq.tryPick chooser source + + /// Generates a list by repeatedly applying a function to a state. + /// A function that takes the current state and returns an option tuple of the next element and the next state. + /// The initial element. + /// The initial state. + /// The result list. let unfold (generator: 'T -> 'State -> ('T * 'State) option) (head: 'T) (state: 'State) = let rec go item state = seq { @@ -462,6 +1035,34 @@ module NonEmptySeq = } go head state |> unsafeOfSeq + /// Splits a sequence of triples into three sequences. + /// The input sequence. + /// A tuple containing the three sequences. + let unzip3 (source: NonEmptySeq<'T1 * 'T2 * 'T3>) : NonEmptySeq<'T1> * NonEmptySeq<'T2> * NonEmptySeq<'T3> = + source |> Seq.toList |> List.unzip3 |> fun (a, b, c) -> (unsafeOfSeq a, unsafeOfSeq b, unsafeOfSeq c) + + /// Updates the element at the specified index. + /// The index of the element to update. + /// The new value. + /// The input sequence. + /// The result sequence. + let updateAt (index: int) (value: 'T) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = + Seq.updateAt index value source |> unsafeOfSeq + + /// Returns a sequence that contains the elements of the sequence for which the given function returns true. + /// A function to test each element of the sequence. + /// The input sequence. + /// The result sequence. + let where (predicate: 'T -> bool) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = + Seq.where predicate source |> unsafeOfSeq + + /// Returns a sequence of sliding windows containing elements drawn from the sequence. + /// The number of elements in each window. + /// The input sequence. + /// The sequence of windows. + let windowed (windowSize: int) (source: NonEmptySeq<'T>) : NonEmptySeq> = + Seq.windowed windowSize source |> Seq.map unsafeOfSeq |> unsafeOfSeq + /// Combines the two sequences into a sequence of pairs. The two sequences need not have equal lengths: /// when one sequence is exhausted any remaining elements in the other /// sequence are ignored. @@ -470,7 +1071,8 @@ module NonEmptySeq = /// The second input sequence. /// /// The result sequence. - let zip (source1: NonEmptySeq<_>) (source2: NonEmptySeq<_>) = Seq.zip source1 source2 |> unsafeOfSeq + let zip (source1: NonEmptySeq<_>) (source2: NonEmptySeq<_>) = + Seq.zip source1 source2 |> unsafeOfSeq /// Combines the three sequences into a sequence of triples. The sequences need not have equal lengths: /// when one sequence is exhausted any remaining elements in the other @@ -481,7 +1083,8 @@ module NonEmptySeq = /// The third input sequence. /// /// The result sequence. - let zip3 (source1: NonEmptySeq<_>) (source2: NonEmptySeq<_>) (source3: NonEmptySeq<_>) = Seq.zip3 source1 source2 source3 |> unsafeOfSeq + let zip3 (source1: NonEmptySeq<_>) (source2: NonEmptySeq<_>) (source3: NonEmptySeq<_>) = + Seq.zip3 source1 source2 source3 |> unsafeOfSeq /// Applies the given function to each element of the NonEmptySequence and concatenates all the /// results. @@ -518,23 +1121,6 @@ module NonEmptySeq = let replace (oldValue: NonEmptySeq<'T>) (newValue: NonEmptySeq<'T>) (source: NonEmptySeq<'T>) : NonEmptySeq<'T> = Seq.replace oldValue newValue source |> unsafeOfSeq - /// Returns a sequence that contains no duplicate entries according to the generic hash and equality comparisons - /// on the keys returned by the given key-generating function. - /// If an element occurs multiple times in the sequence then the later occurrences are discarded. - /// The input sequence. - /// The resulting sequence without duplicates. - let distinct (source: NonEmptySeq<'T>) = source |> Seq.distinct |> ofSeq - - /// Applies a function to each element of the sequence, threading an accumulator argument - /// through the computation. Apply the function to the first two elements of the sequence. - /// Then feed this result into the function along with the third element and so on. - /// Return the final result. If the input function is f and the elements are i0...iN then computes - /// f (... (f i0 i1) i2 ...) iN. - /// The function to reduce two sequence elements to a single element. - /// The input sequence. - /// The final reduced value. - let reduce (reduction: 'T -> 'T -> 'T) source = Seq.reduce reduction source - [] module NonEmptySeqBuilder = diff --git a/tests/FSharpPlus.Tests/Data.fs b/tests/FSharpPlus.Tests/Data.fs index 9dabda490..da91dd202 100644 --- a/tests/FSharpPlus.Tests/Data.fs +++ b/tests/FSharpPlus.Tests/Data.fs @@ -395,7 +395,487 @@ module NonEmptyList = let ``mapi on non empty list should equal mapi on list`` () = let mapOp a b = sprintf "%d-%d" a b fsCheck "list of int" (Prop.forAll (Arb.fromGen NonEmptyListIntOfSeqGen) (fun (q : NonEmptyList, l) -> (q |> NonEmptyList.mapi mapOp |> NonEmptyList.toList) = (l |> List.mapi mapOp))) + + [] + let ``append works`` () = + let list1 = NonEmptyList.create 1 [2; 3] + let list2 = NonEmptyList.create 4 [5; 6] + let result = NonEmptyList.append list1 list2 + Assert.AreEqual(NonEmptyList.create 1 [2; 3; 4; 5; 6], result) + + [] + let ``choose works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.choose (fun x -> if x % 2 = 0 then Some (x * 2) else None) list + Assert.AreEqual(NonEmptyList.create 4 [8], result) + + [] + let ``collect works`` () = + let list = NonEmptyList.create 1 [2; 3] + let result = NonEmptyList.collect (fun x -> NonEmptyList.create x [x * 2]) list + Assert.AreEqual(NonEmptyList.create 1 [2; 2; 4; 3; 6], result) + + [] + let ``concat works`` () = + let lists = NonEmptyList.create (NonEmptyList.create 1 [2]) [NonEmptyList.create 3 [4]; NonEmptyList.create 5 [6]] + let result = NonEmptyList.concat lists + Assert.AreEqual(NonEmptyList.create 1 [2; 3; 4; 5; 6], result) + + [] + let ``contains works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.contains 3 list + Assert.IsTrue(result) + + [] + let ``distinct works`` () = + fsCheck "list of int" (Prop.forAll (Arb.fromGen NonEmptyListIntOfSeqGen) (fun (q, l) -> + (q |> NonEmptyList.distinct |> NonEmptyList.toList) = (l |> List.distinct))) + + [] + let ``exists works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.exists (fun x -> x = 3) list + Assert.IsTrue(result) + + [] + let ``filter works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.filter (fun x -> x % 2 = 0) list + Assert.AreEqual(NonEmptyList.create 2 [4], result) + + [] + let ``find works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.find (fun x -> x = 3) list + Assert.AreEqual(3, result) + + [] + let ``findIndex works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.findIndex (fun x -> x = 3) list + Assert.AreEqual(2, result) + + [] + let ``fold works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.fold (+) 0 list + Assert.AreEqual(10, result) + + [] + let ``foldBack works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.foldBack (+) list 0 + Assert.AreEqual(10, result) + + [] + let ``forall works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.forall (fun x -> x > 0) list + Assert.IsTrue(result) + + [] + let ``groupBy works`` () = + let list = NonEmptyList.create 1 [2; 3; 4; 5] + let result = NonEmptyList.groupBy (fun x -> x % 2) list + let expected = NonEmptyList.create (1, NonEmptyList.create 1 [3; 5]) [(0, NonEmptyList.create 2 [4])] + Assert.AreEqual(expected, result) + [] + let ``indexed works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.indexed list + Assert.AreEqual(NonEmptyList.create (0, 1) [(1, 2); (2, 3); (3, 4)], result) + + [] + let ``init works`` () = + let result = NonEmptyList.init 4 (fun i -> i * 2) + Assert.AreEqual(NonEmptyList.create 0 [2; 4; 6], result) + + [] + let ``insertAt works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.insertAt 2 99 list + Assert.AreEqual(NonEmptyList.create 1 [2; 99; 3; 4], result) + + [] + let ``insertManyAt works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.insertManyAt 2 [99; 100] list + Assert.AreEqual(NonEmptyList.create 1 [2; 99; 100; 3; 4], result) + + [] + let ``item works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.item 2 list + Assert.AreEqual(3, result) + + [] + let ``iter works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let mutable sum = 0 + NonEmptyList.iter (fun x -> sum <- sum + x) list + Assert.AreEqual(10, sum) + + [] + let ``iter2 works`` () = + let list1 = NonEmptyList.create 1 [2; 3] + let list2 = NonEmptyList.create 4 [5; 6] + let mutable sum = 0 + NonEmptyList.iter2 (fun x y -> sum <- sum + x + y) list1 list2 + Assert.AreEqual(21, sum) + + [] + let ``iteri works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let mutable sum = 0 + NonEmptyList.iteri (fun i x -> sum <- sum + i + x) list + Assert.AreEqual(16, sum) + + [] + let ``iteri2 works`` () = + let list1 = NonEmptyList.create 1 [2; 3] + let list2 = NonEmptyList.create 4 [5; 6] + let mutable sum = 0 + NonEmptyList.iteri2 (fun i x y -> sum <- sum + i + x + y) list1 list2 + Assert.AreEqual(24, sum) + + [] + let ``last works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.last list + Assert.AreEqual(4, result) + + [] + let ``map2 works`` () = + let list1 = NonEmptyList.create 1 [2; 3] + let list2 = NonEmptyList.create 4 [5; 6] + let result = NonEmptyList.map2 (fun x y -> x + y) list1 list2 + Assert.AreEqual(NonEmptyList.create 5 [7; 9], result) + + [] + let ``map3 works`` () = + let list1 = NonEmptyList.create 1 [2; 3] + let list2 = NonEmptyList.create 4 [5; 6] + let list3 = NonEmptyList.create 7 [8; 9] + let result = NonEmptyList.map3 (fun x y z -> x + y + z) list1 list2 list3 + Assert.AreEqual(NonEmptyList.create 12 [15; 18], result) + + [] + let ``mapFold works`` () = + let list = NonEmptyList.create 1 [2; 3] + let result, state = NonEmptyList.mapFold (fun acc x -> (x * 2, acc + x)) 0 list + Assert.AreEqual(NonEmptyList.create 2 [4; 6], result) + Assert.AreEqual(6, state) + + [] + let ``mapFoldBack works`` () = + let list = NonEmptyList.create 1 [2; 3] + let result, state = NonEmptyList.mapFoldBack (fun x acc -> (x * 2, acc + x)) list 0 + Assert.AreEqual(NonEmptyList.create 2 [4; 6], result) + Assert.AreEqual(6, state) + + [] + let ``mapi works`` () = + let list = NonEmptyList.create 1 [2; 3] + let result = NonEmptyList.mapi (fun i x -> i + x) list + Assert.AreEqual(NonEmptyList.create 1 [3; 5], result) + + [] + let ``mapi2 works`` () = + let list1 = NonEmptyList.create 1 [2; 3] + let list2 = NonEmptyList.create 4 [5; 6] + let result = NonEmptyList.mapi2 (fun i x y -> i + x + y) list1 list2 + Assert.AreEqual(NonEmptyList.create 5 [8; 11], result) + + [] + let ``max works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.max list + Assert.AreEqual(4, result) + + [] + let ``maxBy works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.maxBy (fun x -> -x) list + Assert.AreEqual(1, result) + + [] + let ``min works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.min list + Assert.AreEqual(1, result) + + [] + let ``minBy works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.minBy (fun x -> -x) list + Assert.AreEqual(4, result) + + [] + let ``pairwise works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.pairwise list + Assert.AreEqual(NonEmptyList.create (1, 2) [(2, 3); (3, 4)], result) + + [] + let ``partition works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result1, result2 = NonEmptyList.partition (fun x -> x % 2 = 0) list + Assert.AreEqual([2; 4], result1) + Assert.AreEqual([1; 3], result2) + + [] + let ``permute works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.permute (fun i -> (i + 1) % 4) list + Assert.AreEqual(NonEmptyList.create 4 [1; 2; 3], result) + + [] + let ``pick works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.pick (fun x -> if x % 2 = 0 then Some (x * 2) else None) list + Assert.AreEqual(4, result) + + [] + let ``removeAt works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.removeAt 2 list + Assert.AreEqual(NonEmptyList.create 1 [2; 4], result) + + [] + let ``removeManyAt works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.removeManyAt 1 2 list + Assert.AreEqual(NonEmptyList.create 1 [4], result) + + [] + let ``replicate works`` () = + let result = NonEmptyList.replicate 4 99 + Assert.AreEqual(NonEmptyList.create 99 [99; 99; 99], result) + + [] + let ``rev works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.rev list + Assert.AreEqual(NonEmptyList.create 4 [3; 2; 1], result) + + [] + let ``scan works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.scan (+) 0 list + Assert.AreEqual(NonEmptyList.create 0 [1; 3; 6; 10], result) + + [] + let ``scanBack works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.scanBack (+) list 0 + Assert.AreEqual(NonEmptyList.create 10 [9; 7; 4; 0], result) + + [] + let ``singleton works`` () = + let result = NonEmptyList.singleton 99 + Assert.AreEqual(NonEmptyList.create 99 [], result) + + [] + let ``skip works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.skip 2 list + Assert.AreEqual(NonEmptyList.create 3 [4], result) + + [] + let ``skipWhile works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.skipWhile (fun x -> x < 3) list + Assert.AreEqual(NonEmptyList.create 3 [4], result) + + [] + let ``sort works`` () = + let list = NonEmptyList.create 3 [1; 4; 2] + let result = NonEmptyList.sort list + Assert.AreEqual(NonEmptyList.create 1 [2; 3; 4], result) + + [] + let ``sortBy works`` () = + let list = NonEmptyList.create 3 [1; 4; 2] + let result = NonEmptyList.sortBy (fun x -> -x) list + Assert.AreEqual(NonEmptyList.create 4 [3; 2; 1], result) + + [] + let ``sortByDescending works`` () = + let list = NonEmptyList.create 3 [1; 4; 2] + let result = NonEmptyList.sortByDescending id list + Assert.AreEqual(NonEmptyList.create 4 [3; 2; 1], result) + + [] + let ``sortDescending works`` () = + let list = NonEmptyList.create 3 [1; 4; 2] + let result = NonEmptyList.sortDescending list + Assert.AreEqual(NonEmptyList.create 4 [3; 2; 1], result) + + [] + let ``sortWith works`` () = + let list = NonEmptyList.create 3 [1; 4; 2] + let result = NonEmptyList.sortWith compare list + Assert.AreEqual(NonEmptyList.create 1 [2; 3; 4], result) + + [] + let ``splitAt works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result1, result2 = NonEmptyList.splitAt 2 list + Assert.AreEqual(NonEmptyList.create 1 [2], result1) + Assert.AreEqual(NonEmptyList.create 3 [4], result2) + + [] + let ``splitInto works`` () = + let list = NonEmptyList.create 1 [2; 3; 4; 5] + let result = NonEmptyList.splitInto 2 list + let expected = NonEmptyList.create (NonEmptyList.create 1 [2; 3]) [NonEmptyList.create 4 [5]] + shoulSeqEqual expected result + + [] + let ``sum works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.sum list + Assert.AreEqual(10, result) + + [] + let ``sumBy works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.sumBy (fun x -> x * 2) list + Assert.AreEqual(20, result) + + [] + let ``tail works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.tail list + Assert.AreEqual(NonEmptyList.create 2 [3; 4], result) + + [] + let ``take works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.take 2 list + Assert.AreEqual(NonEmptyList.create 1 [2], result) + + [] + let ``takeWhile works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.takeWhile (fun x -> x < 3) list + Assert.AreEqual(NonEmptyList.create 1 [2], result) + + [] + let ``transpose works`` () = + let list = NonEmptyList.create (NonEmptyList.create 1 [2]) [NonEmptyList.create 3 [4]] + let result = NonEmptyList.transpose list + Assert.AreEqual(NonEmptyList.create (NonEmptyList.create 1 [3]) [NonEmptyList.create 2 [4]], result) + + [] + let ``truncate works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.truncate 2 list + Assert.AreEqual(NonEmptyList.create 1 [2], result) + + [] + let ``tryExactlyOne works`` () = + let list = NonEmptyList.create 1 [] + let result = NonEmptyList.tryExactlyOne list + Assert.AreEqual(Some 1, result) + + [] + let ``tryFind works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.tryFind (fun x -> x % 2 = 0) list + Assert.AreEqual(Some 2, result) + + [] + let ``tryFindBack works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.tryFindBack (fun x -> x % 2 = 0) list + Assert.AreEqual(Some 4, result) + + [] + let ``tryFindIndex works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.tryFindIndex (fun x -> x % 2 = 0) list + Assert.AreEqual(Some 1, result) + + [] + let ``tryFindIndexBack works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.tryFindIndexBack (fun x -> x % 2 = 0) list + Assert.AreEqual(Some 3, result) + + [] + let ``tryItem works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.tryItem 2 list + Assert.AreEqual(Some 3, result) + + [] + let ``tryLast works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.tryLast list + Assert.AreEqual(Some 4, result) + + [] + let ``tryPick works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.tryPick (fun x -> if x % 2 = 0 then Some (x * 2) else None) list + Assert.AreEqual(Some 4, result) + + [] + let ``unfold works`` () = + let result = NonEmptyList.unfold (fun state -> if state > 3 then None else Some (state, state + 1)) 0 + Assert.AreEqual(NonEmptyList.create 0 [1; 2; 3], result) + + [] + let ``unzip works`` () = + let list = NonEmptyList.create (1, 'a') [(2, 'b'); (3, 'c')] + let result1, result2 = NonEmptyList.unzip list + Assert.AreEqual(NonEmptyList.create 1 [2; 3], result1) + Assert.AreEqual(NonEmptyList.create 'a' ['b'; 'c'], result2) + + [] + let ``unzip3 works`` () = + let list = NonEmptyList.create (1, 'a', true) [(2, 'b', false); (3, 'c', true)] + let result1, result2, result3 = NonEmptyList.unzip3 list + Assert.AreEqual(NonEmptyList.create 1 [2; 3], result1) + Assert.AreEqual(NonEmptyList.create 'a' ['b'; 'c'], result2) + Assert.AreEqual(NonEmptyList.create true [false; true], result3) + + [] + let ``updateAt works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.updateAt 2 99 list + Assert.AreEqual(NonEmptyList.create 1 [2; 99; 4], result) + + [] + let ``where works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.where (fun x -> x % 2 = 0) list + Assert.AreEqual(NonEmptyList.create 2 [4], result) + + [] + let ``windowed works`` () = + let list = NonEmptyList.create 1 [2; 3; 4] + let result = NonEmptyList.windowed 2 list + Assert.AreEqual(NonEmptyList.create (NonEmptyList.create 1 [2]) [NonEmptyList.create 2 [3]; NonEmptyList.create 3 [4]], result) + + [] + let ``zip works`` () = + let list1 = NonEmptyList.create 1 [2; 3] + let list2 = NonEmptyList.create 'a' ['b'; 'c'] + let result = NonEmptyList.zip list1 list2 + Assert.AreEqual(NonEmptyList.create (1, 'a') [(2, 'b'); (3, 'c')], result) + + [] + let ``zip3 works`` () = + let list1 = NonEmptyList.create 1 [2; 3] + let list2 = NonEmptyList.create 'a' ['b'; 'c'] + let list3 = NonEmptyList.create true [false; true] + let result = NonEmptyList.zip3 list1 list2 list3 + Assert.AreEqual(NonEmptyList.create (1, 'a', true) [(2, 'b', false); (3, 'c', true)], result) + module MultiMap = [] let ``monoid works`` () =