Skip to content
Open
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
220 changes: 213 additions & 7 deletions src/FSharp.Collections.Immutable/flat-list.fs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@ 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/dotnet/runtime/blob/54c717a4ed822f46a23893479b8d4398596c041d/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs#L409

Получается самый быстрый вариант – это дёрнуть IsEmpty и выбросить
https://github.com/dotnet/runtime/blob/54c717a4ed822f46a23893479b8d4398596c041d/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Minimal.cs#L157

И получится то же, что они делают. Тогда не нужно использовать их LINQ обёртку

let inline internal checkEmpty (list : FlatList<_>) = check list; if list.Length = 0 then invalidArg (nameof list) "Source is empty" else ()

////////// 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 toList list = check list; Seq.toList list

////////// Building //////////

Expand Down Expand Up @@ -131,6 +135,8 @@ module FlatList =
let sortWith comparer list = sortWithComparer (ComparisonIdentity.FromFunction comparer) list
let sort list = check list; list.Sort()

let get (list:FlatList<_>) index = list.[index]

////////// Loop-based //////////

let inline private builderWithLengthOf list = builderWith <| length list
Expand Down Expand Up @@ -198,18 +204,17 @@ module FlatList =
for i = 0 to len - 1 do
f.Invoke(list1.[i], list2.[i])

let distinctBy projection (list: FlatList<'T>) =
let distinct (list: FlatList<'T>) =
let builder: FlatList<'T>.Builder = builderWith <| length list
let set = System.Collections.Generic.HashSet<'Key>(HashIdentity.Structural)
let mutable outputIndex = 0
let set = System.Collections.Generic.HashSet<'T>(HashIdentity.Structural)

for i = 0 to length list - 1 do
let item = list.[i]
if set.Add <| projection item then
outputIndex <- outputIndex + 1
for item in list do
if set.Add item then
Builder.add item builder

ofBuilder builder

let distinctBy projection (list:FlatList<'a>) = distinct list |> map projection

let map2 mapping list1 list2 =
checkNotDefault (nameof list1) list1
Expand Down Expand Up @@ -400,19 +405,174 @@ module FlatList =
if predicate list.[i] then Some list.[i] else loop (i+1)
loop <| length list - 1

let findIndex predicate list =
check list
let len = length list
let rec loop i =
if i = len then indexNotFound() else
if predicate list.[i] then i else loop (i + 1)
loop 0

let findIndexBack predicate list =
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

let tryFindIndex predicate list =
check list
let len = length list
let rec loop i =
if i = len then None else
if predicate list.[i] then Some i else loop (i + 1)
loop <| 0

let tryFindIndexBack predicate list =
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

let fold folder (state: 'state) (list:FlatList<'a>) =
check list
let mutable result = state
for item in list do
result <- folder result item
result

let scan folder (state: 'state) (list:FlatList<'a>) =
check list
let mutable s = state
let result = builderWithLengthOf list
result.Add s
for item in list do
s <- folder s item
result.Add s
result

let fold2 folder (state: 'state) (left:FlatList<'a>) (right:FlatList<'b>) =
check left
let mutable result = state
let len = length left |> max (length right)
for i = 0 to len - 1 do
result <- folder result left.[i] right.[i]
result

let foldBack2 folder (left:FlatList<'a>) (right:FlatList<'b>) (state:'state) =
check left
let mutable result = state
let leftLen = length left
let rightLen = length right
let len = max leftLen rightLen
for i = 0 to len - 1 do
result <- folder result left.[leftLen - i] right.[rightLen - i]
result

let foldBack folder (list:FlatList<'a>) (state: 'state) =
check list
let mutable result = state
for i = length list - 1 downto 0 do
result <- folder result list.[i]
result

let scanBack folder (list:FlatList<'a>) (state: 'state) =
check list
let mutable s = state
let result = builderWithLengthOf list
result.Add s
for i = length list - 1 downto 0 do
s <- folder s list.[i]
result.Add s
result

let unfold (generator: 'state -> ('a * 'state) option) state =
let builder = FlatListFactory.CreateBuilder()
let mutable s = state
let mutable step = generator s
while step.IsSome do
s <- snd step.Value
fst step.Value |> builder.Add
step <- generator s
ofBuilder builder

let reduce reduction (list:FlatList<'a>) =
checkEmpty list
let mutable result = list.[0]
for i = 1 to length list - 1 do
result <- reduction result list.[i]
result

let reduceBack reduction (list:FlatList<'a>) =
checkEmpty list
let mutable result = list.[list.Length - 1]
for i = length list - 2 downto 0 do
result <- reduction result list.[i]
result

let mapFold mapping (state:'State) (list:FlatList<'T>) =
check list
let builder = builderWithLengthOf list
let mutable outState = state
for item in list do
let item, newState = mapping outState item
builder.Add item
outState <- newState
ofBuilder builder, outState

let mapFoldBack mapping (list:FlatList<'T>) (state:'State) =
check list
let builder = builderWithLengthOf list
let mutable outState = state
for i = length list - 1 downto 0 do
let item, newState = mapping outState list.[i]
builder.Add item
outState <- newState
ofBuilder builder, outState

let zip (left:FlatList<_>) (right:FlatList<_>) =
check left; check right
let len = max (length left) (length right)
let builder = builderWith len
for i = 0 to len do
builder.Add (left.[i], right.[i])
ofBuilder builder

let zip3 (left:FlatList<_>) (middle:FlatList<_>) (right:FlatList<_>) =
check left; check middle; check right
let len = length middle |> max (length left) |> max (length right)
let builder = builderWith len
for i = 0 to len do
builder.Add (left.[i], middle.[i], right.[i])
ofBuilder builder

let internal fst3 (a, _, _) = a
let internal snd3 (_, a, _) = a
let internal thd3 (_, _, a) = a

let unzip list =
let left = builderWithLengthOf list
let right = builderWithLengthOf list
for item in list do
left.Add <| fst item
right.Add <| snd item
left, 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
left, middle, right

let windowed windowSize (list:FlatList<_>) =
check list
raise (new System.NotImplementedException())

// TODO: windowed

////////// Based on other operations //////////
Expand Down Expand Up @@ -469,6 +629,52 @@ module FlatList =
f builder
moveFromBuilder builder

let inline sum ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) =
check list
reduce (+) list

let inline sumBy projection ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) =
check list
list |> map projection |> reduce (+)

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) ) =
check list
let len = length list
let sum = sum list
LanguagePrimitives.DivideByInt sum len

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) ) =
check list
let len = length list
let sum = list |> map projection |> sum
LanguagePrimitives.DivideByInt sum len

let maxBy projection (list:FlatList<'a> when 'a : comparison) = check list; list |> map projection |> reduce max
let minBy projection (list:FlatList<'a> when 'a : comparison) = check list; list |> map projection |> reduce min
let max (list:FlatList<'a> when 'a : comparison) = check list; reduce max list
let min (list:FlatList<'a> when 'a : comparison) = check list; reduce min list

let internal flip f a b = f b a
let internal uncurry f (a, b) = f a b

let sortBy projection = map projection >> sort
let sortInPlaceBy = sortBy
let sortInPlaceWith = sortWith
let sortInPlace = sort
let sortDescending (list:FlatList<'a>) = sortWith (flip LanguagePrimitives.GenericComparison) list
let sortByDescending projection = map projection >> sortDescending

let compareWith comparer (left:FlatList<'a>) (right:FlatList<'b>) = zip left right |> skipWhile ((uncurry comparer) >> ((=) 0)) |> head |> (uncurry comparer)

let tryExactlyOne (list:FlatList<_>) =
checkEmpty list
if length list > 1 then None else Some list.[0]

let exactlyOne list =
match tryExactlyOne list with
| None -> invalidArg (nameof list) "List contains more than one argument"
| Some a -> a

//////////

module ImmutableArray = FlatList