diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..b26cc9a --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,27 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "shell", + "args": [ + "build", + // Ask dotnet build to generate full paths for file names. + "/property:GenerateFullPaths=true", + // Do not generate summary otherwise it leads to duplicate errors in Problems panel + "/consoleloggerparameters:NoSummary" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "silent" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj b/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj index 2b3fbfb..a9177f1 100644 --- a/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj +++ b/src/FSharp.Collections.Immutable/FSharp.Collections.Immutable.fsproj @@ -1,45 +1,41 @@ - - - - netstandard2.0 - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - F# bindings for System.Collections.Immutable - Copyright © XperiAndri 2016 - FSharp.Collections.Immutable - XperiAndri - FSharp.Collections.Immutable - 2.0.0 - XperiAndri;EventHelix;vilinski;anthony-mi;dim-37 - true - FSharp.Collections.Immutable - System;Immutable;Collections;FSharp;F# - git - embedded - https://github.com/fsprojects/FSharp.Collections.Immutable/ - https://github.com/fsprojects/FSharp.Collections.Immutable/ - true - true - - - - - - - - - - - - - - - - true - - - - - - - - + + + netstandard2.0 + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + F# bindings for System.Collections.Immutable + Copyright © XperiAndri 2016 + FSharp.Collections.Immutable + XperiAndri + FSharp.Collections.Immutable + 2.0.0 + XperiAndri;EventHelix;vilinski;anthony-mi;dim-37 + true + FSharp.Collections.Immutable + System;Immutable;Collections;FSharp;F# + git + embedded + https://github.com/fsprojects/FSharp.Collections.Immutable/ + https://github.com/fsprojects/FSharp.Collections.Immutable/ + true + true + + + + + + + + + + + + + + + true + + + + + + \ No newline at end of file diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 5f86c40..30adab3 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -2,6 +2,10 @@ namespace global #else namespace FSharp.Collections.Immutable + +open FSharp.Collections.Immutable +open System.Linq + #endif // The FlatList name comes from a similar construct seen in the official F# source code @@ -16,49 +20,75 @@ module FlatList = let inline internal checkNotDefault argName (list : FlatList<'T>) = if list.IsDefault then invalidArg argName "Uninstantiated ImmutableArray/FlatList" let inline internal check (list : FlatList<'T>) = checkNotDefault (nameof list) list + let inline internal checkEmpty (list : FlatList<_>) = check list; if list.Length = 0 then invalidArg (nameof list) "Source is empty" else () + let inline internal checkAndReturn list = check list; list ////////// Creating ////////// + [] let inline empty<'T> : FlatList<_> = FlatListFactory.Create<'T>() + [] let inline singleton<'T> (item : 'T) : FlatList<'T> = FlatListFactory.Create<'T> (item) + [] + let copy (list:FlatList<_>) = FlatListFactory.CreateRange list + [] let inline ofSeq source = FlatListFactory.CreateRange source + [] let inline ofArray (source : _ array) = FlatListFactory.CreateRange source + [] + let inline ofList (source: _ list) = FlatListFactory.CreateRange source + [] let inline toSeq (flatList: FlatList<_>) = flatList :> seq<_> - let inline toArray (list : FlatList<_>) = check list; Seq.toArray list + [] + let inline toArray (list : FlatList<_>) = ImmutableArrayExtensions.ToArray list + [] + let inline toList list = list |> checkAndReturn |> Seq.toList ////////// Building ////////// + [] let moveFromBuilder (builder : FlatList<_>.Builder) : FlatList<_> = checkNotNull (nameof builder) builder builder.MoveToImmutable() + [] let ofBuilder (builder : FlatList<_>.Builder) : FlatList<_> = checkNotNull (nameof builder) builder builder.ToImmutable() + [] let inline builder () : FlatList<'T>.Builder = FlatListFactory.CreateBuilder() + [] let inline builderWith capacity : FlatList<'T>.Builder = FlatListFactory.CreateBuilder(capacity) + [] let toBuilder list: FlatList<_>.Builder = check list; list.ToBuilder() module Builder = let inline private check (builder: FlatList<'T>.Builder) = checkNotNull (nameof builder) builder + [] let add item builder = check builder; builder.Add(item) let inline internal indexNotFound() = raise <| System.Collections.Generic.KeyNotFoundException() + [] let isEmpty (list: FlatList<_>) = list.IsEmpty + [] let isDefault (list: FlatList<_>) = list.IsDefault + [] let isDefaultOrEmpty (list: FlatList<_>) = list.IsDefaultOrEmpty ////////// IReadOnly* ////////// + [] let length list = check list; list.Length + [] let item index list = check list; list.[index] + [] let append list1 list2 : FlatList<'T> = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 @@ -67,74 +97,96 @@ module FlatList = /// Searches for the specified object and returns the zero-based index of the first occurrence within the range /// of elements in the list that starts at the specified index and /// contains the specified number of elements. + [] let indexRangeWith comparer index count item list = check list list.IndexOf(item, index, count, comparer) + [] let indexRange index count item list = indexRangeWith HashIdentity.Structural index count item list + [] let indexFromWith comparer index item list = indexRangeWith comparer index (length list - index) item + [] let indexFrom index item list = indexFromWith HashIdentity.Structural index item list + [] let indexWith comparer item list = indexFromWith comparer 0 item list + [] let index item list = indexWith HashIdentity.Structural item list /// Searches for the specified object and returns the zero-based index of the last occurrence within the /// range of elements in the list that contains the specified number /// of elements and ends at the specified index. + [] let lastIndexRangeWith comparer index count item list = check list list.LastIndexOf(item, index, count, comparer) + [] let lastIndexRange index count item list = lastIndexRangeWith HashIdentity.Structural index count item list + [] let lastIndexFromWith comparer index item list = lastIndexRangeWith comparer index (index + 1) item list + [] let lastIndexFrom index item list = lastIndexFromWith HashIdentity.Structural index item list + [] let lastIndexWith comparer item list = lastIndexFromWith comparer (length list - 1) item list + [] let lastIndex item list = lastIndexWith HashIdentity.Structural item list /// Removes the specified objects from the list with the given comparer. + [] let removeAllWith (comparer: System.Collections.Generic.IEqualityComparer<_>) items list: FlatList<_> = check list list.RemoveRange(items, comparer) /// Removes the specified objects from the list. + [] let removeAll items list = removeAllWith HashIdentity.Structural items list /// Removes all the elements that do not match the conditions defined by the specified predicate. - let filter predicate list: FlatList<_> = - check list - System.Predicate(not << predicate) - |> list.RemoveAll + [] + let filter predicate list = ImmutableArrayExtensions.Where (list, predicate) /// Removes all the elements that do not match the conditions defined by the specified predicate. - let where predicate list = filter predicate list + [] + let where predicate list = ImmutableArrayExtensions.Where (list, predicate) /// Removes a range of elements from the list. + [] let removeRange index (count: int) list: FlatList<_> = check list; list.RemoveRange(index, count) + [] let blit source sourceIndex (destination: 'T[]) destinationIndex count = checkNotDefault (nameof source) source try source.CopyTo(sourceIndex, destination, destinationIndex, count) with exn -> raise exn // throw same exception with the correct stack trace. Update exception code + [] let sortRangeWithComparer comparer index count list = check list list.Sort(index, count, comparer) + [] let sortRangeWith comparer index count list = sortRangeWithComparer (ComparisonIdentity.FromFunction comparer) index count list + [] let sortRange index count list = sortRangeWithComparer ComparisonIdentity.Structural index count list + [] let sortWithComparer (comparer : System.Collections.Generic.IComparer<_>) list = check list; list.Sort(comparer) + [] let sortWith comparer list = sortWithComparer (ComparisonIdentity.FromFunction comparer) list + [] let sort list = check list; list.Sort() ////////// Loop-based ////////// let inline private builderWithLengthOf list = builderWith <| length list + [] let init count initializer = if count < 0 then invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative let builder = builderWith count @@ -142,230 +194,111 @@ module FlatList = builder.Add <| initializer i moveFromBuilder builder - let rec private concatAddLengths (arrs: FlatList>) i acc = - if i >= length arrs then acc - else concatAddLengths arrs (i+1) (acc + arrs.[i].Length) + [] + let initWithValue count value = + if count < 0 then invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative + let builder = builderWith count + for i = 0 to count - 1 do + builder.Add value + ofBuilder builder - let concat (arrs : FlatList>) = // consider generalizing - let result: FlatList<'T>.Builder = builderWith <| concatAddLengths arrs 0 0 - for i = 0 to length arrs - 1 do - result.AddRange(arrs.[i]: FlatList<'T>) - moveFromBuilder result + [] + let concat (seqs:FlatList>) = + let builder = seqs |> Seq.map length |> Seq.sum |> builderWith + for seq in seqs do + builder.AddRange seq + ofBuilder builder - let inline map mapping list = - check list - let builder = builderWithLengthOf list - for i = 0 to length list - 1 do - builder.Add(mapping list.[i]) - moveFromBuilder builder + [] + let map<'a, 'b> (mapping: 'a -> 'b) list = ImmutableArrayExtensions.Select (list, System.Func<'a, 'b> mapping) - let countBy projection list = - check list - // need struct box optimization - let dict = new System.Collections.Generic.Dictionary<'Key, int>(HashIdentity.Structural) - - // Build the groupings - for v in list do - let key = projection v - let mutable prev = Unchecked.defaultof<_> - if dict.TryGetValue(key, &prev) then dict.[key] <- prev + 1 else dict.[key] <- 1 - - let res = builderWith dict.Count - let mutable i = 0 - for group in dict do - res.Add(group.Key, group.Value) - i <- i + 1 - moveFromBuilder res - - let indexed list = - check list - let builder = builderWithLengthOf list - for i = 0 to length list - 1 do - builder.Add(i, list.[i]) - moveFromBuilder builder + [] + let countBy projection = checkAndReturn >> Seq.countBy projection - let inline iter action list = - check list - for i = 0 to length list - 1 do - action list.[i] + [] + let indexed list = list |> checkAndReturn |> Seq.indexed + + [] + let iter action = checkAndReturn >> Seq.iter action + [] let iter2 action list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 - let f = OptimizedClosures.FSharpFunc<'T,'U, unit>.Adapt(action) - let len = length list1 - if len <> length list2 then invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths - for i = 0 to len - 1 do - f.Invoke(list1.[i], list2.[i]) - - let distinctBy projection (list: FlatList<'T>) = - let builder: FlatList<'T>.Builder = builderWith <| length list - let set = System.Collections.Generic.HashSet<'Key>(HashIdentity.Structural) - let mutable outputIndex = 0 - - for i = 0 to length list - 1 do - let item = list.[i] - if set.Add <| projection item then - outputIndex <- outputIndex + 1 - Builder.add item builder + Seq.iter2 action list1 list2 - ofBuilder builder + [] + let distinct (list: FlatList<'T>) = list |> Seq.distinct + + [] + let distinctBy projection = checkAndReturn >> Seq.distinctBy projection + [] let map2 mapping list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 - let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(mapping) - let len1 = list1.Length - if len1 <> list2.Length then invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths - let res = builderWith len1 - for i = 0 to len1 - 1 do - res.Add <| f.Invoke(list1.[i], list2.[i]) - moveFromBuilder res + Seq.map2 mapping list1 list2 + [] let map3 mapping list1 list2 list3 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 checkNotDefault (nameof list3) list3 - let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt(mapping) - let len1 = list1.Length - if not (len1 = list2.Length) - then invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths - if not (len1 = list3.Length) - then invalidArg (nameof list3) ErrorStrings.ListsHaveDifferentLengths - - let res = builderWith len1 - for i = 0 to len1 - 1 do - res.Add <| f.Invoke(list1.[i], list2.[i], list3.[i]) - moveFromBuilder res + Seq.map3 mapping list1 list2 list3 + + + [] let mapi2 mapping list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 - let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt(mapping) - let len1 = list1.Length - if len1 <> list2.Length then invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths - let res = builderWith len1 - for i = 0 to len1 - 1 do - res.Add <| f.Invoke(i,list1.[i], list2.[i]) - moveFromBuilder res - - let iteri action list = - check list - let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(action) - let len = list.Length - for i = 0 to len - 1 do - f.Invoke(i, list.[i]) + Seq.mapi2 mapping list1 list2 + + [] + let iteri action = checkAndReturn >> Seq.iteri action + [] let iteri2 action list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 - let f = OptimizedClosures.FSharpFunc<_,_,_,_>.Adapt(action) - let len1 = list1.Length - if len1 <> list2.Length then invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths - for i = 0 to len1 - 1 do - f.Invoke(i,list1.[i], list2.[i]) + Seq.iteri2 action list1 list2 - let mapi mapping list = - check list - let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(mapping) - let len = list.Length - let res = builderWithLengthOf list - for i = 0 to len - 1 do - res.Add <| f.Invoke(i,list.[i]) - moveFromBuilder res - - let exists predicate list = - check list - let len = list.Length - let rec loop i = i < len && (predicate list.[i] || loop (i+1)) - loop 0 + [] + let mapi mapping = checkAndReturn >> Seq.mapi mapping - let inline contains e list = - check list - let mutable state = false - let mutable i = 0 - while (not state && i < list.Length) do - state <- e = list.[i] - i <- i + 1 - state + [] + let exists predicate list = ImmutableArrayExtensions.Any (list, System.Func<'a, bool> predicate) + [] + let contains e = checkAndReturn >> Seq.contains e + + [] let exists2 predicate list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 - let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(predicate) - let len1 = list1.Length - if len1 <> list2.Length then invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths - let rec loop i = i < len1 && (f.Invoke(list1.[i], list2.[i]) || loop (i+1)) - loop 0 + Seq.exists2 predicate list1 list2 - let forall predicate list = - check list - let len = list.Length - let rec loop i = i >= len || (predicate list.[i] && loop (i+1)) - loop 0 + [] + let forall predicate list = ImmutableArrayExtensions.All (list, System.Func<'a, bool> predicate) + [] let forall2 predicate list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 - let f = OptimizedClosures.FSharpFunc<_,_,_>.Adapt(predicate) - let len1 = list1.Length - if len1 <> list2.Length then invalidArg (nameof list2) ErrorStrings.ListsHaveDifferentLengths - let rec loop i = i >= len1 || (f.Invoke(list1.[i], list2.[i]) && loop (i+1)) - loop 0 + Seq.forall2 predicate list1 list2 - let groupBy projection list = - check list - let dict = new System.Collections.Generic.Dictionary<'Key,ResizeArray<'T>>(HashIdentity.Structural) - - // Build the groupings - for i = 0 to (list.Length - 1) do - let v = list.[i] - let key = projection v - let ok, prev = dict.TryGetValue(key) - if ok then - prev.Add(v) - else - let prev = new ResizeArray<'T>(1) - dict.[key] <- prev - prev.Add(v) - - // Return the list-of-lists. - let result = builderWith dict.Count - let mutable i = 0 - for group in dict do - result.Add(group.Key, ofSeq group.Value) - i <- i + 1 - - moveFromBuilder result - - let pick chooser list = - check list - let rec loop i = - if i >= list.Length then - indexNotFound() - else - match chooser list.[i] with - | None -> loop(i+1) - | Some res -> res - loop 0 - - let tryPick chooser list = - check list - let rec loop i = - if i >= list.Length then None else - match chooser list.[i] with - | None -> loop(i+1) - | res -> res - loop 0 - - let choose chooser list = - check list - let res = builderWith list.Length - for i = 0 to list.Length - 1 do - match chooser list.[i] with - | None -> () - | Some b -> res.Add(b) - ofBuilder res + [] + let groupBy projection = checkAndReturn >> Seq.groupBy projection + + [] + let pick chooser = checkAndReturn >> Seq.pick chooser + + [] + let tryPick chooser = checkAndReturn >> Seq.tryPick chooser + [] + let choose chooser = checkAndReturn >> Seq.choose chooser + + [] let partition predicate list = check list let res1 = builderWith list.Length @@ -375,48 +308,124 @@ module FlatList = if predicate x then res1.Add(x) else res2.Add(x) ofBuilder res1, ofBuilder res2 - let find predicate list = - check list - let rec loop i = - if i >= list.Length then indexNotFound() else - if predicate list.[i] then list.[i] else loop (i+1) - loop 0 - let tryFind predicate list = + let rec private tryFindWithCustomStride (list:FlatList<_>) index predicate indexPredicate indexTransform = + if indexPredicate index then + if predicate list.[index] then + Some (index, list.[index]) + else tryFindWithCustomStride list (indexTransform index) predicate indexPredicate indexTransform + else None + + [] + let tryFindItem predicate direction list = check list - let rec loop i = - if i >= list.Length then None else - if predicate list.[i] then Some list.[i] else loop (i+1) - loop 0 - let findBack predicate list = + let startIndex = if direction then 0 else length list - 1 + let indexPredicate = if direction then ((>) (length list)) else ((<=) 0) + let transform = if direction then ((+) 1) else ((+) -1) // because section is not available + tryFindWithCustomStride list startIndex predicate indexPredicate transform + + [] + let find predicate = checkAndReturn >> Seq.find predicate + [] + let tryFind predicate = checkAndReturn >> Seq.tryFind predicate + [] + let findBack predicate = checkAndReturn >> Seq.findBack predicate + [] + let tryFindBack predicate = checkAndReturn >> Seq.tryFindBack predicate + [] + let findIndex predicate = checkAndReturn >> Seq.findIndex predicate + [] + let findIndexBack predicate = checkAndReturn >> Seq.findIndexBack predicate + [] + let tryFindIndex predicate = checkAndReturn >> Seq.tryFindIndex predicate + [] + let tryFindIndexBack predicate = checkAndReturn >> Seq.tryFindIndexBack predicate + + [] + let fold folder (state: 'state) = checkAndReturn >> Seq.fold folder state + + [] + let scan folder (state: 'state) = checkAndReturn >> Seq.scan folder state + + [] + let fold2 folder (state: 'state) (left:FlatList<'a>) (right:FlatList<'b>) = + check left; check right + Seq.fold2 folder state left right + + [] + let foldBack2 folder (left:FlatList<'a>) (right:FlatList<'b>) (state:'state) = + check left; check right + Seq.foldBack2 folder left right state + + [] + let foldBack folder (list:FlatList<'a>) (state: 'state) = check list - let rec loop i = - if i < 0 then indexNotFound() else - if predicate list.[i] then list.[i] else loop (i - 1) - loop <| length list - 1 - let tryFindBack predicate list = + Seq.foldBack folder list state + + [] + let scanBack folder (list:FlatList<'a>) (state:'state) = check list - let rec loop i = - if i < 0 then None else - if predicate list.[i] then Some list.[i] else loop (i+1) - loop <| length list - 1 + Seq.scanBack folder list state + + [] + let unfold (generator: 'state -> ('a * 'state) option) state = + Seq.unfold generator state + + [] + let reduce reduction = checkAndReturn >> Seq.reduce reduction - let findIndexBack predicate list = + [] + let reduceBack reduction = checkAndReturn >> Seq.reduceBack reduction + + [] + let mapFold mapping (state:'State) (list:FlatList<'T>) = check list - let rec loop i = - if i < 0 then indexNotFound() else - if predicate list.[i] then i else loop (i - 1) - loop <| length list - 1 + Seq.mapFold mapping state list - let tryFindIndexBack predicate list = + [] + let mapFoldBack mapping (list:FlatList<'T>) (state:'State) = check list - let rec loop i = - if i < 0 then None else - if predicate list.[i] then Some i else loop (i - 1) - loop <| length list - 1 - // TODO: windowed + Seq.mapFoldBack mapping list state + + [] + let zip (left:FlatList<_>) (right:FlatList<_>) = + check left; check right + Seq.zip left right + + [] + let zip3 (left:FlatList<_>) (middle:FlatList<_>) (right:FlatList<_>) = + check left; check middle; check right + Seq.zip3 left middle right + + [] + let unzip list = + let left = builderWithLengthOf list + let right = builderWithLengthOf list + for item in list do + left.Add <| fst item + right.Add <| snd item + ofBuilder left, ofBuilder right + + [] + let unzip3 list = + let left = builderWithLengthOf list + let right = builderWithLengthOf list + let middle = builderWithLengthOf list + for item in list do + left.Add <| fst3 item + middle.Add <| snd3 item + right.Add <| thd3 item + ofBuilder left, ofBuilder middle, ofBuilder right + + [] + let windowed windowSize = checkAndReturn >> Seq.windowed windowSize + + [] + let fill target targetIndex count value = + mapi (fun i a -> if targetIndex <= i && i < targetIndex + count then value else a) target ////////// Based on other operations ////////// + [] let take count list = removeRange count (length list - count) list let inline private lengthWhile predicate list = @@ -425,49 +434,121 @@ module FlatList = while count < list.Length && predicate list.[count] do count <- count + 1 count + + [] let takeWhile predicate list = take (lengthWhile predicate list) list + [] let skip index list = removeRange 0 index list + [] let skipWhile predicate list = skip (lengthWhile predicate list) list + [] let sub start stop list = skip start list |> take (stop - start - 1) + [] let truncate count list = if count < length list then take count list else list + [] let splitAt index list = take index list, skip index list + [] let head list = item 0 list + [] let tryItem index list = if index >= length list || index < 0 then None else Some(list.[index]) + [] let tryHead list = tryItem 0 list + [] let last (list : FlatList<_>) = list.[length list - 1] + [] let tryLast list = tryItem (length list - 1) list - let tail list = removeRange 1 (length list - 1) list + [] + let tail list = skip 1 list + [] let tryTail list = if isEmpty list then None else Some <| tail list - let create count item = init count <| fun _ -> item // optimize - - let replicate count item = create item count - - let collect mapping list = concat <| map mapping list - - let inline build f = - let builder = builder() - f builder - moveFromBuilder builder - - let inline update f list = - let builder = toBuilder list - f builder - moveFromBuilder builder + [] + let create = initWithValue + + [] + let replicate item = item |> flip initWithValue + + [] + let collect mapping list = + ImmutableArrayExtensions.SelectMany (list, System.Func<'a, 'a seq> mapping, System.Func<'a,'a,'a> (fun _ -> id)) + + [] + let inline sum ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) = + list |> checkAndReturn |> reduce (+) + + [] + let inline sumBy projection ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) = + list |> checkAndReturn |> map projection |> Seq.sum + + [] + let inline average ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member DivideByInt : ^T*int -> ^T) and ^T : (static member Zero : ^T) ) = + list |> checkAndReturn |> applyOverFuncs LanguagePrimitives.DivideByInt sum length + + [] + let inline averageBy projection ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member DivideByInt : ^T*int -> ^T) and ^T : (static member Zero : ^T) ) = + list |> checkAndReturn |> applyOverFuncs LanguagePrimitives.DivideByInt ((map projection) >> Seq.sum) length + + let private minMaxReduction projection comparison a b = + let pa = projection a + let pb = projection b + if comparison pa pb then a else b + + [] + let maxBy projection (list:FlatList<'a>) = list |> checkAndReturn |> reduce (minMaxReduction projection (>)) + + [] + let minBy projection (list:FlatList<'a> when 'a : comparison) = list |> checkAndReturn |> reduce (minMaxReduction projection (<)) + + [] + let max (list:FlatList<'a> when 'a : comparison) = list |> checkAndReturn |> Seq.max + [] + let min (list:FlatList<'a> when 'a : comparison) = list |> checkAndReturn |> Seq.min + + [] + let sortBy projection = sortWith (applyOverArgs LanguagePrimitives.GenericComparison projection) + [] + let sortDescending (list:FlatList<'a>) = sortWith (flip LanguagePrimitives.GenericComparison) list + [] + let sortByDescending projection = sortWith (flip (applyOverArgs LanguagePrimitives.GenericComparison projection)) + + [] + let compareWith comparer (left:FlatList<'a>) (right:FlatList<'b>) = zip left right |> Seq.skipWhile ((uncurry comparer) >> ((=) 0)) |> Seq.head |> (uncurry comparer) + + [] + let tryExactlyOne (list:FlatList<_>) = Seq.tryExactlyOne list + [] + let exactlyOne (list:FlatList<_>) = Seq.exactlyOne list + + [] + let rev (list:FlatList<_>) = list |> checkAndReturn |> Seq.rev + [] + let transpose (list:FlatList<_>) = list |> checkAndReturn |> Seq.transpose + [] + let permute indexMap (list:FlatList<_>) = list |> checkAndReturn |> Seq.permute indexMap + [] + let pairwise (list:FlatList<_>) = list |> checkAndReturn |> Seq.pairwise + [] + let except itemsToExclude (list:FlatList<_>) = list |> checkAndReturn |> Seq.except itemsToExclude + [] + let splitInto count (list:FlatList<_>) = list |> checkAndReturn |> Seq.splitInto count + [] + let chunkBySize chunkSize (list:FlatList<_>) = list |> checkAndReturn |> Seq.chunkBySize chunkSize + [] + let allPairs (left:FlatList<'a>) (right:FlatList<'b>) = Seq.allPairs (checkAndReturn left) (checkAndReturn right) ////////// diff --git a/src/FSharp.Collections.Immutable/functional-utils.fs b/src/FSharp.Collections.Immutable/functional-utils.fs new file mode 100644 index 0000000..ac6f0d1 --- /dev/null +++ b/src/FSharp.Collections.Immutable/functional-utils.fs @@ -0,0 +1,17 @@ +#if INTERACTIVE +namespace global +#else +namespace FSharp.Collections.Immutable +#endif + +[] +module internal FunctionalUtils = + let inline flip f a b = f b a + let inline uncurry f (a, b) = f a b + + let inline applyOverFuncs f g h x = f (g x) (h x) + let inline applyOverArgs f g x y = f (g x) (g y) + + let inline fst3 (a, _, _) = a + let inline snd3 (_, a, _) = a + let inline thd3 (_, _, a) = a \ No newline at end of file