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