From 1d13c8b0e8d3382f1af0df6521b248ce295f449d Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Wed, 19 May 2021 14:33:36 +0300 Subject: [PATCH 01/24] flat list - first set of missing array APIs --- src/FSharp.Collections.Immutable/flat-list.fs | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 5f86c40..1bbeddf 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -16,6 +16,7 @@ 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 () ////////// Creating ////////// @@ -131,6 +132,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 @@ -400,6 +403,14 @@ 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 = @@ -413,6 +424,63 @@ module FlatList = 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 -> 'a -> 'state) (state: 'state) (list:FlatList<'a>) = + check list + let mutable result = state + for i = 0 to length list do + result <- folder result list.[i] + result + + let foldBack (folder : 'state -> 'a -> 'state) (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 reduce (reduction : 'a -> 'a -> 'a) (list:FlatList<'a>) = + checkEmpty list + let mutable result = list.[0] + for i = 1 to length list do + result <- reduction result list.[i] + result + + let reduceBack (reduction : 'a -> 'a -> 'a) (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 -> 'T -> 'Result * 'State) (state:'State) (list:FlatList<'T>) : 'Result[] * 'State = + check list + raise (new System.NotImplementedException()) + + let mapFoldBack (mapping:'State -> 'T -> 'Result * 'State) (list:FlatList<'T>) (state:'State) : 'Result[] * 'State = + check list + raise (new System.NotImplementedException()) + + 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 windowed windowSize (list:FlatList<_>) = + check list + raise (new System.NotImplementedException()) + // TODO: windowed ////////// Based on other operations ////////// @@ -469,6 +537,36 @@ 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 sortBy projection (list:FlatList<'a>) = list |> map projection |> sort + let sortInPlaceBy = sortBy + let sortInPlaceWith = sortWith + let sortInPlace = sort + ////////// module ImmutableArray = FlatList From f2b637d248985fddf6fde8db0a9c9647edc6b817 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Wed, 19 May 2021 14:41:02 +0300 Subject: [PATCH 02/24] flat list - fix mapFold(+Back) and for loops --- src/FSharp.Collections.Immutable/flat-list.fs | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 1bbeddf..b3dff46 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -428,8 +428,8 @@ module FlatList = let fold (folder : 'state -> 'a -> 'state) (state: 'state) (list:FlatList<'a>) = check list let mutable result = state - for i = 0 to length list do - result <- folder result list.[i] + for item in list do + result <- folder result item result let foldBack (folder : 'state -> 'a -> 'state) (list:FlatList<'a>) (state: 'state) = @@ -442,7 +442,7 @@ module FlatList = let reduce (reduction : 'a -> 'a -> 'a) (list:FlatList<'a>) = checkEmpty list let mutable result = list.[0] - for i = 1 to length list do + for i = 1 to length list - 1 do result <- reduction result list.[i] result @@ -453,13 +453,25 @@ module FlatList = result <- reduction result list.[i] result - let mapFold (mapping:'State -> 'T -> 'Result * 'State) (state:'State) (list:FlatList<'T>) : 'Result[] * 'State = + let mapFold (mapping:'State -> 'T -> 'Result * 'State) (state:'State) (list:FlatList<'T>) = check list - raise (new System.NotImplementedException()) + 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:'State -> 'T -> 'Result * 'State) (list:FlatList<'T>) (state:'State) : 'Result[] * 'State = + let mapFoldBack (mapping:'State -> 'T -> 'Result * 'State) (list:FlatList<'T>) (state:'State) = check list - raise (new System.NotImplementedException()) + 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 From f68ab1c797b6c45b1127a68ccb4153970e97e208 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Wed, 19 May 2021 14:46:22 +0300 Subject: [PATCH 03/24] flat list - add unzip --- src/FSharp.Collections.Immutable/flat-list.fs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index b3dff46..94738b5 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -488,7 +488,31 @@ module FlatList = for i = 0 to len do builder.Add (left.[i], middle.[i], right.[i]) ofBuilder builder - + + let internal fst (a, _) = a + let internal snd (_, a) = a + let internal fst3 (a, _, _) = a + let internal snd3 (_, a, _) = a + let internal thd3 (_, _, a) = a + + let unzip (list:FlatList<'a*'b>) = + 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:FlatList<'a*'b*'c>) = + 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, right + let windowed windowSize (list:FlatList<_>) = check list raise (new System.NotImplementedException()) From 96466f7886621f4ae194466f0d14bf5eb23dd8c9 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Wed, 19 May 2021 14:57:53 +0300 Subject: [PATCH 04/24] flat list - find index, unfold, remove excessions --- src/FSharp.Collections.Immutable/flat-list.fs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 94738b5..2d9faa7 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -407,7 +407,7 @@ module FlatList = check list let len = length list let rec loop i = - if i > len then indexNotFound() else + if i = len then indexNotFound() else if predicate list.[i] then i else loop (i + 1) loop 0 @@ -418,6 +418,14 @@ module FlatList = 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 = @@ -438,6 +446,16 @@ module FlatList = for i = length list - 1 downto 0 do result <- folder result list.[i] 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 : 'a -> 'a -> 'a) (list:FlatList<'a>) = checkEmpty list @@ -489,8 +507,6 @@ module FlatList = builder.Add (left.[i], middle.[i], right.[i]) ofBuilder builder - let internal fst (a, _) = a - let internal snd (_, a) = a let internal fst3 (a, _, _) = a let internal snd3 (_, a, _) = a let internal thd3 (_, _, a) = a From 6ce003bdae3856591ea35d46eb8d61bb0fc0e690 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Wed, 19 May 2021 16:58:35 +0300 Subject: [PATCH 05/24] flat list - another changes pack - conversions - plain distinct based on distinctBy - scans and folds - signatures simplifications - fix unzip3 - sorting/comparing --- src/FSharp.Collections.Immutable/flat-list.fs | 81 +++++++++++++++---- 1 file changed, 64 insertions(+), 17 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 2d9faa7..198b94a 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -22,12 +22,15 @@ module FlatList = 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 ////////// @@ -201,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 @@ -433,19 +435,57 @@ module FlatList = if predicate list.[i] then Some i else loop (i - 1) loop <| length list - 1 - let fold (folder : 'state -> 'a -> 'state) (state: 'state) (list:FlatList<'a>) = + 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 foldBack (folder : 'state -> 'a -> 'state) (list:FlatList<'a>) (state: 'state) = + 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() @@ -457,21 +497,21 @@ module FlatList = step <- generator s ofBuilder builder - let reduce (reduction : 'a -> 'a -> 'a) (list:FlatList<'a>) = + 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 : 'a -> 'a -> 'a) (list:FlatList<'a>) = + 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 -> 'T -> 'Result * 'State) (state:'State) (list:FlatList<'T>) = + let mapFold mapping (state:'State) (list:FlatList<'T>) = check list let builder = builderWithLengthOf list let mutable outState = state @@ -481,7 +521,7 @@ module FlatList = outState <- newState ofBuilder builder, outState - let mapFoldBack (mapping:'State -> 'T -> 'Result * 'State) (list:FlatList<'T>) (state:'State) = + let mapFoldBack mapping (list:FlatList<'T>) (state:'State) = check list let builder = builderWithLengthOf list let mutable outState = state @@ -511,7 +551,7 @@ module FlatList = let internal snd3 (_, a, _) = a let internal thd3 (_, _, a) = a - let unzip (list:FlatList<'a*'b>) = + let unzip list = let left = builderWithLengthOf list let right = builderWithLengthOf list for item in list do @@ -519,7 +559,7 @@ module FlatList = right.Add <| snd item left, right - let unzip3 (list:FlatList<'a*'b*'c>) = + let unzip3 list = let left = builderWithLengthOf list let right = builderWithLengthOf list let middle = builderWithLengthOf list @@ -527,7 +567,7 @@ module FlatList = left.Add <| fst3 item middle.Add <| snd3 item right.Add <| thd3 item - left, right + left, middle, right let windowed windowSize (list:FlatList<_>) = check list @@ -614,11 +654,18 @@ module FlatList = 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 sortBy projection (list:FlatList<'a>) = list |> map projection |> sort + 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) + ////////// module ImmutableArray = FlatList From 515f913e61fd804649a0eb554f7f29eb7ccf2b8b Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Wed, 19 May 2021 17:02:02 +0300 Subject: [PATCH 06/24] flat list - exactlyOne + try --- src/FSharp.Collections.Immutable/flat-list.fs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 198b94a..921630a 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -665,7 +665,16 @@ module FlatList = 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 From 82dbf1d7f138e4b5589093f02593a21b4e172d48 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Thu, 20 May 2021 07:54:21 +0300 Subject: [PATCH 07/24] flat-list - post-review fixes for first half --- src/FSharp.Collections.Immutable/flat-list.fs | 177 +++++------------- 1 file changed, 42 insertions(+), 135 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 921630a..356d00d 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -17,6 +17,7 @@ module FlatList = 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 raiseOrReturn list = check list; list ////////// Creating ////////// @@ -204,17 +205,17 @@ module FlatList = for i = 0 to len - 1 do f.Invoke(list1.[i], list2.[i]) - let distinct (list: FlatList<'T>) = - let builder: FlatList<'T>.Builder = builderWith <| length list - let set = System.Collections.Generic.HashSet<'T>(HashIdentity.Structural) + let distinct (list: FlatList<'T>) = list |> System.Collections.Generic.HashSet |> ofSeq + + let distinctBy projection (list:FlatList<'a>) = + let builder = builderWithLengthOf list + let set = System.Collections.Generic.HashSet<'key>(HashIdentity.Structural) for item in list do - if set.Add item then - Builder.add item builder - + if set.Add <| projection item then + builder.Add item + ofBuilder builder - - let distinctBy projection (list:FlatList<'a>) = distinct list |> map projection let map2 mapping list1 list2 = checkNotDefault (nameof list1) list1 @@ -405,13 +406,7 @@ 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 findIndex predicate = raiseOrReturn >> Seq.findIndex predicate let findIndexBack predicate list = check list @@ -420,13 +415,7 @@ module FlatList = 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 tryFindIndex predicate = raiseOrReturn >> Seq.tryFindIndex predicate let tryFindIndexBack predicate list = check list @@ -435,117 +424,50 @@ module FlatList = 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 fold folder (state: 'state) = raiseOrReturn >> Seq.fold folder state - 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 scan folder (state: 'state) = raiseOrReturn >> Seq.scan folder state >> ofSeq 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 + check left; check right + Seq.fold2 folder state left right 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 + check left; check right + Seq.foldBack2 folder left right state 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 + Seq.foldBack folder list state - let scanBack folder (list:FlatList<'a>) (state: 'state) = + 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 + Seq.scanBack folder list state |> ofSeq 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 + Seq.unfold generator state |> ofSeq - 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 reduce reduction = raiseOrReturn >> Seq.reduce reduction - 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 reduceBack reduction = raiseOrReturn >> Seq.reduceBack reduction 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 (items, s) = Seq.mapFold mapping state list + ofSeq items, s 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 (i, s) = Seq.mapFoldBack mapping list state + ofSeq i, s 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 + Seq.zip left right |> ofSeq 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 + Seq.zip3 left middle right |> ofSeq let internal fst3 (a, _, _) = a let internal snd3 (_, a, _) = a @@ -569,11 +491,7 @@ module FlatList = right.Add <| thd3 item left, middle, right - let windowed windowSize (list:FlatList<_>) = - check list - raise (new System.NotImplementedException()) - - // TODO: windowed + let windowed windowSize = raiseOrReturn >> Seq.windowed windowSize >> Seq.map ofSeq >> ofSeq ////////// Based on other operations ////////// @@ -630,29 +548,23 @@ module FlatList = 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 + list |> raiseOrReturn |> reduce (+) 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 (+) + list |> raiseOrReturn |> map projection |> reduce (+) + + let internal over f g h x = f (g x) (h x) 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 + list |> raiseOrReturn |> over 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) ) = - check list - let len = length list - let sum = list |> map projection |> sum - LanguagePrimitives.DivideByInt sum len + list |> raiseOrReturn |> over LanguagePrimitives.DivideByInt (map projection >> sum) length - 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 maxBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> map projection |> reduce max + let minBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> map projection |> reduce min + let max (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce max + let min (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce min let internal flip f a b = f b a let internal uncurry f (a, b) = f a b @@ -666,14 +578,9 @@ module FlatList = 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 tryExactlyOne = Seq.tryExactlyOne - let exactlyOne list = - match tryExactlyOne list with - | None -> invalidArg (nameof list) "List contains more than one argument" - | Some a -> a + let exactlyOne = Seq.exactlyOne ////////// From b5dc6df1e58e4725349b2adb4fc15531c788ebf7 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Thu, 20 May 2021 09:30:32 +0300 Subject: [PATCH 08/24] flat list - fix sorting (and helpers) --- src/FSharp.Collections.Immutable/flat-list.fs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 356d00d..6cbcd05 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -553,13 +553,14 @@ module FlatList = let inline sumBy projection ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) = list |> raiseOrReturn |> map projection |> reduce (+) - let internal over f g h x = f (g x) (h x) + let inline internal applyOverFuncs f g h x = f (g x) (h x) + let inline internal applyOverArgs f g x y = f (g x) (g y) 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 |> raiseOrReturn |> over LanguagePrimitives.DivideByInt sum length + list |> raiseOrReturn |> 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 |> raiseOrReturn |> over LanguagePrimitives.DivideByInt (map projection >> sum) length + list |> raiseOrReturn |> applyOverFuncs LanguagePrimitives.DivideByInt (map projection >> sum) length let maxBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> map projection |> reduce max let minBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> map projection |> reduce min @@ -569,12 +570,12 @@ module FlatList = 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 sortBy projection = sortWith (applyOverArgs LanguagePrimitives.GenericComparison projection) 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 sortByDescending projection = sortWith (flip (applyOverArgs LanguagePrimitives.GenericComparison projection)) let compareWith comparer (left:FlatList<'a>) (right:FlatList<'b>) = zip left right |> skipWhile ((uncurry comparer) >> ((=) 0)) |> head |> (uncurry comparer) From 3b9c912e360a1fdca3e528c680b1ebf8850418da Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Thu, 20 May 2021 09:38:43 +0300 Subject: [PATCH 09/24] general: vscode build task, move common funcs --- .vscode/tasks.json | 27 ++++++ .../FSharp.Collections.Immutable.fsproj | 86 +++++++++---------- src/FSharp.Collections.Immutable/flat-list.fs | 10 --- .../functional-utils.fs | 17 ++++ 4 files changed, 85 insertions(+), 55 deletions(-) create mode 100644 .vscode/tasks.json create mode 100644 src/FSharp.Collections.Immutable/functional-utils.fs 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 6cbcd05..4042ebe 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -469,10 +469,6 @@ module FlatList = check left; check middle; check right Seq.zip3 left middle right |> ofSeq - 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 @@ -553,9 +549,6 @@ module FlatList = let inline sumBy projection ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) = list |> raiseOrReturn |> map projection |> reduce (+) - let inline internal applyOverFuncs f g h x = f (g x) (h x) - let inline internal applyOverArgs f g x y = f (g x) (g y) - 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 |> raiseOrReturn |> applyOverFuncs LanguagePrimitives.DivideByInt sum length @@ -567,9 +560,6 @@ module FlatList = let max (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce max let min (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce min - let internal flip f a b = f b a - let internal uncurry f (a, b) = f a b - let sortBy projection = sortWith (applyOverArgs LanguagePrimitives.GenericComparison projection) let sortInPlaceBy = sortBy let sortInPlaceWith = sortWith 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 From 3cc77b14fc865656ec5a0f31b80d0a45c8b86c5e Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Thu, 20 May 2021 13:15:08 +0300 Subject: [PATCH 10/24] flat list - remove sortInPlace* --- src/FSharp.Collections.Immutable/flat-list.fs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 4042ebe..7ecb3bb 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -561,9 +561,6 @@ module FlatList = let min (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce min let sortBy projection = sortWith (applyOverArgs LanguagePrimitives.GenericComparison projection) - let sortInPlaceBy = sortBy - let sortInPlaceWith = sortWith - let sortInPlace = sort let sortDescending (list:FlatList<'a>) = sortWith (flip LanguagePrimitives.GenericComparison) list let sortByDescending projection = sortWith (flip (applyOverArgs LanguagePrimitives.GenericComparison projection)) From b200e5caf6ef4cdd897ead02edfc10fb1193f6fb Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Thu, 20 May 2021 13:35:37 +0300 Subject: [PATCH 11/24] flat list - all APIs ported --- src/FSharp.Collections.Immutable/flat-list.fs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 7ecb3bb..4b3019c 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -149,6 +149,8 @@ module FlatList = builder.Add <| initializer i moveFromBuilder builder + let zeroCreate<'a> count = init count (fun _ -> Unchecked.defaultof<'a>) + let rec private concatAddLengths (arrs: FlatList>) i acc = if i >= length arrs then acc else concatAddLengths arrs (i+1) (acc + arrs.[i].Length) @@ -489,6 +491,11 @@ module FlatList = let windowed windowSize = raiseOrReturn >> Seq.windowed windowSize >> Seq.map ofSeq >> ofSeq + let fill target targetIndex count value = + indexed target + |> map (fun (i, a) -> if targetIndex <= i && i < targetIndex + count then value else a) + |> ofSeq + ////////// Based on other operations ////////// let take count list = removeRange count (length list - count) list @@ -567,9 +574,17 @@ module FlatList = let compareWith comparer (left:FlatList<'a>) (right:FlatList<'b>) = zip left right |> skipWhile ((uncurry comparer) >> ((=) 0)) |> head |> (uncurry comparer) let tryExactlyOne = Seq.tryExactlyOne - let exactlyOne = Seq.exactlyOne + let rev = Seq.rev >> ofSeq + let transpose = Seq.transpose >> Seq.map ofSeq >> ofSeq + let permute indexMap = Seq.permute indexMap >> ofSeq + let pairwise = Seq.pairwise >> ofSeq + let except itemsToExclude = Seq.except itemsToExclude >> ofSeq + let splitInto count = Seq.splitInto count >> Seq.map ofSeq >> ofSeq + let chunkBySize chunkSize = Seq.chunkBySize chunkSize >> Seq.map ofSeq >> ofSeq + let allPairs left = Seq.allPairs left >> ofSeq + ////////// module ImmutableArray = FlatList From bbf1366c69259ed35a02c94eb7660aab5cd8f889 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Thu, 20 May 2021 14:41:59 +0300 Subject: [PATCH 12/24] flat list - post-review fixes --- src/FSharp.Collections.Immutable/flat-list.fs | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 4b3019c..4b74221 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -149,8 +149,6 @@ module FlatList = builder.Add <| initializer i moveFromBuilder builder - let zeroCreate<'a> count = init count (fun _ -> Unchecked.defaultof<'a>) - let rec private concatAddLengths (arrs: FlatList>) i acc = if i >= length arrs then acc else concatAddLengths arrs (i+1) (acc + arrs.[i].Length) @@ -209,15 +207,7 @@ module FlatList = let distinct (list: FlatList<'T>) = list |> System.Collections.Generic.HashSet |> ofSeq - let distinctBy projection (list:FlatList<'a>) = - let builder = builderWithLengthOf list - let set = System.Collections.Generic.HashSet<'key>(HashIdentity.Structural) - - for item in list do - if set.Add <| projection item then - builder.Add item - - ofBuilder builder + let distinctBy projection (list:FlatList<'a>) = Seq.distinctBy projection >> ofSeq let map2 mapping list1 list2 = checkNotDefault (nameof list1) list1 @@ -477,7 +467,7 @@ module FlatList = for item in list do left.Add <| fst item right.Add <| snd item - left, right + ofBuilder left, ofBuilder right let unzip3 list = let left = builderWithLengthOf list @@ -487,7 +477,7 @@ module FlatList = left.Add <| fst3 item middle.Add <| snd3 item right.Add <| thd3 item - left, middle, right + ofBuilder left, ofBuilder middle, ofBuilder right let windowed windowSize = raiseOrReturn >> Seq.windowed windowSize >> Seq.map ofSeq >> ofSeq @@ -562,8 +552,8 @@ module FlatList = 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 |> raiseOrReturn |> applyOverFuncs LanguagePrimitives.DivideByInt (map projection >> sum) length - let maxBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> map projection |> reduce max - let minBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> map projection |> reduce min + let maxBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> Seq.map projection |> Seq.reduce max + let minBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> Seq.map projection |> Seq.reduce min let max (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce max let min (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce min @@ -571,10 +561,10 @@ module FlatList = 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 |> skipWhile ((uncurry comparer) >> ((=) 0)) |> head |> (uncurry comparer) + let compareWith comparer (left:FlatList<'a>) (right:FlatList<'b>) = zip left right |> Seq.skipWhile ((uncurry comparer) >> ((=) 0)) |> Seq.head |> (uncurry comparer) - let tryExactlyOne = Seq.tryExactlyOne - let exactlyOne = Seq.exactlyOne + let tryExactlyOne (list:FlatList<_>) = Seq.tryExactlyOne list + let exactlyOne (list:FlatList<_>) = Seq.exactlyOne list let rev = Seq.rev >> ofSeq let transpose = Seq.transpose >> Seq.map ofSeq >> ofSeq From 0ee37d53683ee18029c5666e81ac4ce951ef00ba Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Thu, 20 May 2021 15:52:32 +0300 Subject: [PATCH 13/24] flat list - update signatures: force main arg as FlatList --- src/FSharp.Collections.Immutable/flat-list.fs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 4b74221..2363937 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -550,7 +550,7 @@ module FlatList = list |> raiseOrReturn |> 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 |> raiseOrReturn |> applyOverFuncs LanguagePrimitives.DivideByInt (map projection >> sum) length + list |> raiseOrReturn |> applyOverFuncs LanguagePrimitives.DivideByInt ((map projection) >> sum) length let maxBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> Seq.map projection |> Seq.reduce max let minBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> Seq.map projection |> Seq.reduce min @@ -566,14 +566,14 @@ module FlatList = let tryExactlyOne (list:FlatList<_>) = Seq.tryExactlyOne list let exactlyOne (list:FlatList<_>) = Seq.exactlyOne list - let rev = Seq.rev >> ofSeq - let transpose = Seq.transpose >> Seq.map ofSeq >> ofSeq - let permute indexMap = Seq.permute indexMap >> ofSeq - let pairwise = Seq.pairwise >> ofSeq - let except itemsToExclude = Seq.except itemsToExclude >> ofSeq - let splitInto count = Seq.splitInto count >> Seq.map ofSeq >> ofSeq - let chunkBySize chunkSize = Seq.chunkBySize chunkSize >> Seq.map ofSeq >> ofSeq - let allPairs left = Seq.allPairs left >> ofSeq + let rev = raiseOrReturn >> Seq.rev >> ofSeq + let transpose = raiseOrReturn >> Seq.transpose >> Seq.map ofSeq >> ofSeq + let permute indexMap = raiseOrReturn >> Seq.permute indexMap >> ofSeq + let pairwise = raiseOrReturn >> Seq.pairwise >> ofSeq + let except itemsToExclude = raiseOrReturn >> Seq.except itemsToExclude >> ofSeq + let splitInto count = raiseOrReturn >> Seq.splitInto count >> Seq.map ofSeq >> ofSeq + let chunkBySize chunkSize = raiseOrReturn >> Seq.chunkBySize chunkSize >> Seq.map ofSeq >> ofSeq + let allPairs (left:FlatList<'a>) (right:FlatList<'b>) = Seq.allPairs (raiseOrReturn left) (raiseOrReturn right) |> ofSeq ////////// From 697c5f85a0347b4c6487181a9aaaaf5a9147e400 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Fri, 21 May 2021 09:30:07 +0300 Subject: [PATCH 14/24] flat-list - changes - remove excessive `get` - add initialization with value (and use in `create`/`replicate`) - use `Seq` in majority of functions - simplify `fill` - fix signatures --- src/FSharp.Collections.Immutable/flat-list.fs | 234 ++++-------------- 1 file changed, 43 insertions(+), 191 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 2363937..73ef59a 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -136,8 +136,6 @@ 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 @@ -149,22 +147,20 @@ module FlatList = builder.Add <| initializer i moveFromBuilder builder + 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 rec private concatAddLengths (arrs: FlatList>) i acc = if i >= length arrs then acc else concatAddLengths arrs (i+1) (acc + arrs.[i].Length) - 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:'a seq seq) = seqs |> Seq.concat |> ofSeq - 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 inline map mapping = raiseOrReturn >> Seq.map mapping >> ofSeq let countBy projection list = check list @@ -191,10 +187,7 @@ module FlatList = builder.Add(i, list.[i]) moveFromBuilder builder - let inline iter action list = - check list - for i = 0 to length list - 1 do - action list.[i] + let inline iter action = raiseOrReturn >> Seq.iter action let iter2 action list1 list2 = checkNotDefault (nameof list1) list1 @@ -212,157 +205,51 @@ module FlatList = 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 |> ofSeq 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 |> ofSeq + 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 + Seq.mapi2 mapping list1 list2 |> ofSeq - 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]) + let iteri action = raiseOrReturn >> 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 mapi mapping = raiseOrReturn >> Seq.mapi mapping >> ofSeq - 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 exists predicate = raiseOrReturn >> Seq.exists predicate - 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 inline contains e = raiseOrReturn >> 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 = raiseOrReturn >> Seq.forall 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) + let groupBy projection = raiseOrReturn >> Seq.groupBy projection >> Seq.map (fun (k, i) -> k, ofSeq i) >> ofSeq - // 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 + let pick chooser = raiseOrReturn >> Seq.pick chooser - moveFromBuilder result + let tryPick chooser = raiseOrReturn >> Seq.tryPick chooser - 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 choose chooser = raiseOrReturn >> Seq.choose chooser >> ofSeq let partition predicate list = check list @@ -373,48 +260,14 @@ 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 = - 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 = - 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 = - 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 - + let find predicate = raiseOrReturn >> Seq.find predicate + let tryFind predicate = raiseOrReturn >> Seq.tryFind predicate + let findBack predicate = raiseOrReturn >> Seq.findBack predicate + let tryFindBack predicate = raiseOrReturn >> Seq.tryFindBack predicate let findIndex predicate = raiseOrReturn >> Seq.findIndex predicate - - 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 findIndexBack predicate = raiseOrReturn >> Seq.findIndexBack predicate let tryFindIndex predicate = raiseOrReturn >> Seq.tryFindIndex predicate - - 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 tryFindIndexBack predicate = raiseOrReturn >> Seq.tryFindIndexBack predicate let fold folder (state: 'state) = raiseOrReturn >> Seq.fold folder state @@ -482,8 +335,7 @@ module FlatList = let windowed windowSize = raiseOrReturn >> Seq.windowed windowSize >> Seq.map ofSeq >> ofSeq let fill target targetIndex count value = - indexed target - |> map (fun (i, a) -> if targetIndex <= i && i < targetIndex + count then value else a) + mapi (fun i a -> if targetIndex <= i && i < targetIndex + count then value else a) target |> ofSeq ////////// Based on other operations ////////// @@ -520,13 +372,13 @@ module FlatList = 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 create = initWithValue - let replicate count item = create item count + let replicate item = item |> flip initWithValue let collect mapping list = concat <| map mapping list @@ -566,13 +418,13 @@ module FlatList = let tryExactlyOne (list:FlatList<_>) = Seq.tryExactlyOne list let exactlyOne (list:FlatList<_>) = Seq.exactlyOne list - let rev = raiseOrReturn >> Seq.rev >> ofSeq - let transpose = raiseOrReturn >> Seq.transpose >> Seq.map ofSeq >> ofSeq - let permute indexMap = raiseOrReturn >> Seq.permute indexMap >> ofSeq - let pairwise = raiseOrReturn >> Seq.pairwise >> ofSeq - let except itemsToExclude = raiseOrReturn >> Seq.except itemsToExclude >> ofSeq - let splitInto count = raiseOrReturn >> Seq.splitInto count >> Seq.map ofSeq >> ofSeq - let chunkBySize chunkSize = raiseOrReturn >> Seq.chunkBySize chunkSize >> Seq.map ofSeq >> ofSeq + let rev (list:FlatList<_>) = list |> raiseOrReturn |> Seq.rev |> ofSeq + let transpose (list:FlatList<_>) = list |> raiseOrReturn |> Seq.transpose |> Seq.map ofSeq |> ofSeq + let permute indexMap (list:FlatList<_>) = list |> raiseOrReturn |> Seq.permute indexMap |> ofSeq + let pairwise (list:FlatList<_>) = list |> raiseOrReturn |> Seq.pairwise |> ofSeq + let except itemsToExclude (list:FlatList<_>) = list |> raiseOrReturn |> Seq.except itemsToExclude |> ofSeq + let splitInto count (list:FlatList<_>) = list |> raiseOrReturn |> Seq.splitInto count |> Seq.map ofSeq |> ofSeq + let chunkBySize chunkSize (list:FlatList<_>) = list |> raiseOrReturn |> Seq.chunkBySize chunkSize |> Seq.map ofSeq |> ofSeq let allPairs (left:FlatList<'a>) (right:FlatList<'b>) = Seq.allPairs (raiseOrReturn left) (raiseOrReturn right) |> ofSeq ////////// From 8af69b9e496d35d11a567bc9c31fa8deb9bdf6bf Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Fri, 21 May 2021 09:37:19 +0300 Subject: [PATCH 15/24] flat list - fix minor issues - change `countBy`, `indexed`, `iter2` to use `Seq` - fix distinctBy (remove excessive arg + use `raiseOrReturn`) --- src/FSharp.Collections.Immutable/flat-list.fs | 63 ++++++------------- 1 file changed, 19 insertions(+), 44 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 73ef59a..658f2ea 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -162,45 +162,20 @@ module FlatList = let inline map mapping = raiseOrReturn >> Seq.map mapping >> ofSeq - 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 = raiseOrReturn >> Seq.countBy projection >> ofSeq + + let indexed list = list |> raiseOrReturn |> Seq.indexed |> ofSeq let inline iter action = raiseOrReturn >> 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]) + Seq.iter2 action list1 list2 let distinct (list: FlatList<'T>) = list |> System.Collections.Generic.HashSet |> ofSeq - - let distinctBy projection (list:FlatList<'a>) = Seq.distinctBy projection >> ofSeq + + let distinctBy projection = raiseOrReturn >> Seq.distinctBy projection >> ofSeq let map2 mapping list1 list2 = checkNotDefault (nameof list1) list1 @@ -223,7 +198,7 @@ module FlatList = let iteri2 action list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 - Seq.iteri2 action list1 list2 + Seq.iteri2 action list1 list2 let mapi mapping = raiseOrReturn >> Seq.mapi mapping >> ofSeq @@ -268,32 +243,32 @@ module FlatList = let findIndexBack predicate = raiseOrReturn >> Seq.findIndexBack predicate let tryFindIndex predicate = raiseOrReturn >> Seq.tryFindIndex predicate let tryFindIndexBack predicate = raiseOrReturn >> Seq.tryFindIndexBack predicate - + let fold folder (state: 'state) = raiseOrReturn >> Seq.fold folder state - + let scan folder (state: 'state) = raiseOrReturn >> Seq.scan folder state >> ofSeq - + 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 Seq.foldBack folder list state - + let scanBack folder (list:FlatList<'a>) (state:'state) = check list Seq.scanBack folder list state |> ofSeq let unfold (generator: 'state -> ('a * 'state) option) state = Seq.unfold generator state |> ofSeq - + let reduce reduction = raiseOrReturn >> Seq.reduce reduction - + let reduceBack reduction = raiseOrReturn >> Seq.reduceBack reduction let mapFold mapping (state:'State) (list:FlatList<'T>) = @@ -305,11 +280,11 @@ module FlatList = check list let (i, s) = Seq.mapFoldBack mapping list state ofSeq i, s - + let zip (left:FlatList<_>) (right:FlatList<_>) = check left; check right Seq.zip left right |> ofSeq - + let zip3 (left:FlatList<_>) (middle:FlatList<_>) (right:FlatList<_>) = check left; check middle; check right Seq.zip3 left middle right |> ofSeq @@ -392,10 +367,10 @@ 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) ) = + let inline sum ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) = list |> raiseOrReturn |> reduce (+) - let inline sumBy projection ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) = + let inline sumBy projection ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) = list |> raiseOrReturn |> 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) ) = From 452bfb9aea46bf36be2a83a4cdfb514eaf4b5f79 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Fri, 21 May 2021 14:14:53 +0300 Subject: [PATCH 16/24] flat list - final post-review changes --- src/FSharp.Collections.Immutable/flat-list.fs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 658f2ea..502037a 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -148,7 +148,7 @@ module FlatList = moveFromBuilder builder let initWithValue count value = - if count < 0 then invalidArg(nameof count) ErrorStrings.InputMustBeNonNegative + if count < 0 then invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative let builder = builderWith count for i = 0 to count - 1 do builder.Add value @@ -160,20 +160,20 @@ module FlatList = let concat (seqs:'a seq seq) = seqs |> Seq.concat |> ofSeq - let inline map mapping = raiseOrReturn >> Seq.map mapping >> ofSeq + let map mapping = raiseOrReturn >> Seq.map mapping >> ofSeq let countBy projection = raiseOrReturn >> Seq.countBy projection >> ofSeq let indexed list = list |> raiseOrReturn |> Seq.indexed |> ofSeq - let inline iter action = raiseOrReturn >> Seq.iter action + let iter action = raiseOrReturn >> Seq.iter action let iter2 action list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 Seq.iter2 action list1 list2 - let distinct (list: FlatList<'T>) = list |> System.Collections.Generic.HashSet |> ofSeq + let distinct (list: FlatList<'T>) = list |> Seq.distinct |> ofSeq let distinctBy projection = raiseOrReturn >> Seq.distinctBy projection >> ofSeq @@ -204,7 +204,7 @@ module FlatList = let exists predicate = raiseOrReturn >> Seq.exists predicate - let inline contains e = raiseOrReturn >> Seq.contains e + let contains e = raiseOrReturn >> Seq.contains e let exists2 predicate list1 list2 = checkNotDefault (nameof list1) list1 From 119b2245e666da6be9293b44c4725d22e2f5c234 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Mon, 24 May 2021 10:41:32 +0300 Subject: [PATCH 17/24] flat list tests - first pack --- .../FSharp.Collections.Immutable.Tests.fsproj | 26 ++++++ FSharp.Collections.Immutable.Tests/Program.fs | 5 ++ .../flat-list-tests.fs | 88 +++++++++++++++++++ FSharp.Collections.Immutable.sln | 6 ++ src/FSharp.Collections.Immutable/flat-list.fs | 2 +- .../functional-utils.fs | 6 +- 6 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj create mode 100644 FSharp.Collections.Immutable.Tests/Program.fs create mode 100644 FSharp.Collections.Immutable.Tests/flat-list-tests.fs diff --git a/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj b/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj new file mode 100644 index 0000000..ad19202 --- /dev/null +++ b/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj @@ -0,0 +1,26 @@ + + + + net5.0 + + false + false + + + + + + + + + + + + + + + + + + + diff --git a/FSharp.Collections.Immutable.Tests/Program.fs b/FSharp.Collections.Immutable.Tests/Program.fs new file mode 100644 index 0000000..8d7f915 --- /dev/null +++ b/FSharp.Collections.Immutable.Tests/Program.fs @@ -0,0 +1,5 @@ +open Fuchu + +module Program = + let [] main = defaultMainThisAssembly + diff --git a/FSharp.Collections.Immutable.Tests/flat-list-tests.fs b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs new file mode 100644 index 0000000..c220930 --- /dev/null +++ b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs @@ -0,0 +1,88 @@ +module FSharp.Collections.Immutable.Tests + +open NUnit.Framework +open Fuchu +open FSharp.Collections.Immutable + +let nullList<'a> = new FlatList<'a>() +let emptyList<'a> = FlatList<'a>.Empty +let list0to9 = FlatList.init 10 id +let noopPredicate _ = true +let ignore2 _ _ = () +let ignore3 _ _ _ = () + +let throwsOn item func = + fun () -> + Assert.Catch (fun () -> func item) + |> ignore + +let doesNotThrowOn item func = fun () -> Assert.DoesNotThrow (fun () -> func item) + +let throwsOnlyOnNullLists func = [ + testCase "throws on null list" ((func >> ignore) |> throwsOn nullList) + testCase "does not throw on empty list" ((func >> ignore) |> doesNotThrowOn emptyList) + testCase "does not throw on non-empty list" ((func >> ignore) |> doesNotThrowOn list0to9) +] + +let doesNotThrowOnlyOnFilledList func = [ + testCase "throws on null list" ((func >> ignore) |> throwsOn nullList) + testCase "throws on empty list" ((func >> ignore) |> throwsOn emptyList) + testCase "does not throw on non-empty list" ((func >> ignore) |> doesNotThrowOn list0to9) +] + +[] +let flatListTests = + testList "FlatList" [ + testList "toArray" (FlatList.toArray |> throwsOnlyOnNullLists) + testList "toList" (FlatList.toList |> throwsOnlyOnNullLists) + testList "toSeq" (FlatList.toSeq |> throwsOnlyOnNullLists) + testList "length" (FlatList.length |> throwsOnlyOnNullLists) + testList "append (first arg)" ((fun a -> FlatList.append a emptyList |> ignore) |> throwsOnlyOnNullLists) + testList "append (second arg)" ((FlatList.append emptyList) |> throwsOnlyOnNullLists) + testList "indexFromWith" ((FlatList.indexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> throwsOnlyOnNullLists) + testList "indexFrom" ((FlatList.indexFrom 0 1) |> throwsOnlyOnNullLists) + testList "indexWith" ((FlatList.indexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> throwsOnlyOnNullLists) + testList "index" ((FlatList.index 1) |> throwsOnlyOnNullLists) + testList "removeAllWith" ((FlatList.removeAllWith LanguagePrimitives.FastGenericEqualityComparer emptyList) |> throwsOnlyOnNullLists) + testList "removeAll" ((FlatList.removeAll emptyList) |> throwsOnlyOnNullLists) + testList "filter" ((FlatList.filter noopPredicate) |> throwsOnlyOnNullLists) + testList "where" ((FlatList.where noopPredicate) |> throwsOnlyOnNullLists) + testList "sortWithComparer" ((FlatList.sortWithComparer LanguagePrimitives.FastGenericComparer) |> throwsOnlyOnNullLists) + testList "sortWith" ((FlatList.sortWith LanguagePrimitives.GenericComparison) |> throwsOnlyOnNullLists) + testList "sort" (FlatList.sort |> throwsOnlyOnNullLists) + testList "map" (FlatList.map noopPredicate |> throwsOnlyOnNullLists) + testList "countBy" (FlatList.countBy noopPredicate |> throwsOnlyOnNullLists) + testList "indexed" (FlatList.indexed |> throwsOnlyOnNullLists) + testList "iter" (FlatList.iter ignore |> throwsOnlyOnNullLists) + testList "iter2 (first arg)" ((fun a -> FlatList.iter2 ignore2 a emptyList) |> throwsOnlyOnNullLists) + testList "iter2 (second arg)" ((FlatList.iter2 ignore2 emptyList) |> throwsOnlyOnNullLists) + testList "distinct" (FlatList.distinct |> throwsOnlyOnNullLists) + testList "distinctBy" (FlatList.distinctBy noopPredicate |> throwsOnlyOnNullLists) + testList "map2 (first arg)" ((fun a -> FlatList.map2 ignore2 a emptyList) |> throwsOnlyOnNullLists) + testList "map2 (second arg)" ((FlatList.map2 ignore2 emptyList) |> throwsOnlyOnNullLists) + testList "map3 (first arg)" ((fun a -> FlatList.map3 ignore3 a emptyList emptyList) |> throwsOnlyOnNullLists) + testList "map3 (second arg)" ((fun a -> FlatList.map3 ignore3 a emptyList emptyList) |> throwsOnlyOnNullLists) + testList "map3 (third arg)" ((FlatList.map3 ignore3 emptyList emptyList) |> throwsOnlyOnNullLists) + testList "mapi2 (first arg)" ((fun a -> FlatList.mapi2 ignore3 a emptyList) |> throwsOnlyOnNullLists) + testList "mapi2 (second arg)" ((FlatList.mapi2 ignore3 emptyList) |> throwsOnlyOnNullLists) + testList "iteri" ((FlatList.iteri ignore2) |> throwsOnlyOnNullLists) + testList "iteri2 (first arg)" ((fun a -> FlatList.iteri2 ignore3 a emptyList) |> throwsOnlyOnNullLists) + testList "iteri2 (second arg)" ((FlatList.iteri2 ignore3 emptyList) |> throwsOnlyOnNullLists) + testList "mapi" ((FlatList.mapi ignore2) |> throwsOnlyOnNullLists) + + testList "item" ((FlatList.item 0) |> doesNotThrowOnlyOnFilledList) + testList "indexRangeWith" ((FlatList.indexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList) + testList "indexRange" ((FlatList.indexRange 0 1 1) |> doesNotThrowOnlyOnFilledList) + testList "lastIndexRangeWith" ((FlatList.lastIndexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList) + testList "lastIndexRange" ((FlatList.lastIndexRange 0 1 1) |> doesNotThrowOnlyOnFilledList) + testList "lastIndexFromWith" ((FlatList.lastIndexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> doesNotThrowOnlyOnFilledList) + testList "lastIndexFrom" ((FlatList.lastIndexFrom 0 1) |> doesNotThrowOnlyOnFilledList) + testList "lastIndexWith" ((FlatList.lastIndexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> doesNotThrowOnlyOnFilledList) + testList "lastIndex" ((FlatList.lastIndex 1) |> doesNotThrowOnlyOnFilledList) + testList "removeRange" ((FlatList.removeRange 0 1) |> doesNotThrowOnlyOnFilledList) + testList "blit" ((fun a -> FlatList.blit a 0 [|10;11;12|] 0 1) |> doesNotThrowOnlyOnFilledList) + testList "sortRangeWithComparer" ((FlatList.sortRangeWithComparer LanguagePrimitives.FastGenericComparer 0 1) |> doesNotThrowOnlyOnFilledList) + testList "sortRangeWith" ((FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 1) |> doesNotThrowOnlyOnFilledList) + testList "sortRange" ((FlatList.sortRange 0 1) |> doesNotThrowOnlyOnFilledList) + + ] diff --git a/FSharp.Collections.Immutable.sln b/FSharp.Collections.Immutable.sln index a9324bb..f053b66 100644 --- a/FSharp.Collections.Immutable.sln +++ b/FSharp.Collections.Immutable.sln @@ -11,6 +11,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .github\workflows\publish_release.yml = .github\workflows\publish_release.yml EndProjectSection EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Collections.Immutable.Tests", "FSharp.Collections.Immutable.Tests\FSharp.Collections.Immutable.Tests.fsproj", "{2272CACE-DBCE-4BC8-BADD-11802BAF30EC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {9805E74C-D028-4D05-9256-5C2FDC9B6EA8}.Debug|Any CPU.Build.0 = Debug|Any CPU {9805E74C-D028-4D05-9256-5C2FDC9B6EA8}.Release|Any CPU.ActiveCfg = Release|Any CPU {9805E74C-D028-4D05-9256-5C2FDC9B6EA8}.Release|Any CPU.Build.0 = Release|Any CPU + {2272CACE-DBCE-4BC8-BADD-11802BAF30EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2272CACE-DBCE-4BC8-BADD-11802BAF30EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2272CACE-DBCE-4BC8-BADD-11802BAF30EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2272CACE-DBCE-4BC8-BADD-11802BAF30EC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 502037a..c2b990b 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -29,7 +29,7 @@ module FlatList = 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 toSeq (flatList: FlatList<_>) = check flatList; flatList :> seq<_> let inline toArray (list : FlatList<_>) = check list; Seq.toArray list let inline toList list = check list; Seq.toList list diff --git a/src/FSharp.Collections.Immutable/functional-utils.fs b/src/FSharp.Collections.Immutable/functional-utils.fs index ac6f0d1..e05821b 100644 --- a/src/FSharp.Collections.Immutable/functional-utils.fs +++ b/src/FSharp.Collections.Immutable/functional-utils.fs @@ -5,13 +5,13 @@ namespace FSharp.Collections.Immutable #endif [] -module internal FunctionalUtils = +module 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 + let inline thd3 (_, _, a) = a From 49414df67be04cfe3e18b18f572a1a123aa4b951 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Mon, 24 May 2021 10:42:17 +0300 Subject: [PATCH 18/24] flat list tests - fix invocation --- FSharp.Collections.Immutable.Tests/Program.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FSharp.Collections.Immutable.Tests/Program.fs b/FSharp.Collections.Immutable.Tests/Program.fs index 8d7f915..657e7d5 100644 --- a/FSharp.Collections.Immutable.Tests/Program.fs +++ b/FSharp.Collections.Immutable.Tests/Program.fs @@ -1,5 +1,5 @@ open Fuchu module Program = - let [] main = defaultMainThisAssembly + let [] main args = defaultMainThisAssembly args From c020a360c0486bd87739685a68b1c74005edbc93 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Tue, 8 Jun 2021 09:44:11 +0300 Subject: [PATCH 19/24] add compiled name annotations --- src/FSharp.Collections.Immutable/flat-list.fs | 176 +++++++++++++++++- 1 file changed, 172 insertions(+), 4 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 502037a..1c7c3cb 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -2,6 +2,9 @@ namespace global #else namespace FSharp.Collections.Immutable + +open FSharp.Collections.Immutable + #endif // The FlatList name comes from a similar construct seen in the official F# source code @@ -21,49 +24,70 @@ module FlatList = ////////// 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 ////////// + [] 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 @@ -72,74 +96,99 @@ 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 /// Removes all the elements that do not match the conditions defined by the specified predicate. + [] let where predicate list = filter predicate list /// 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 @@ -147,6 +196,7 @@ module FlatList = builder.Add <| initializer i moveFromBuilder builder + [] let initWithValue count value = if count < 0 then invalidArg (nameof count) ErrorStrings.InputMustBeNonNegative let builder = builderWith count @@ -158,74 +208,103 @@ module FlatList = if i >= length arrs then acc else concatAddLengths arrs (i+1) (acc + arrs.[i].Length) - let concat (seqs:'a seq seq) = seqs |> Seq.concat |> ofSeq + [] + let concat (seqs:FlatList>) = + let builder = builderWith <| concatAddLengths seqs 0 0 + for seq in seqs do + builder.AddRange seq + ofBuilder builder + [] let map mapping = raiseOrReturn >> Seq.map mapping >> ofSeq + [] let countBy projection = raiseOrReturn >> Seq.countBy projection >> ofSeq + [] let indexed list = list |> raiseOrReturn |> Seq.indexed |> ofSeq + [] let iter action = raiseOrReturn >> Seq.iter action + [] let iter2 action list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 Seq.iter2 action list1 list2 + [] let distinct (list: FlatList<'T>) = list |> Seq.distinct |> ofSeq + [] let distinctBy projection = raiseOrReturn >> Seq.distinctBy projection >> ofSeq + [] let map2 mapping list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 Seq.map2 mapping list1 list2 |> ofSeq + [] let map3 mapping list1 list2 list3 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 checkNotDefault (nameof list3) list3 Seq.map3 mapping list1 list2 list3 |> ofSeq + + [] let mapi2 mapping list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 Seq.mapi2 mapping list1 list2 |> ofSeq + [] let iteri action = raiseOrReturn >> Seq.iteri action + [] let iteri2 action list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 Seq.iteri2 action list1 list2 + [] let mapi mapping = raiseOrReturn >> Seq.mapi mapping >> ofSeq + [] let exists predicate = raiseOrReturn >> Seq.exists predicate + [] let contains e = raiseOrReturn >> Seq.contains e + [] let exists2 predicate list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 Seq.exists2 predicate list1 list2 + [] let forall predicate = raiseOrReturn >> Seq.forall predicate + [] let forall2 predicate list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 Seq.forall2 predicate list1 list2 + [] let groupBy projection = raiseOrReturn >> Seq.groupBy projection >> Seq.map (fun (k, i) -> k, ofSeq i) >> ofSeq + [] let pick chooser = raiseOrReturn >> Seq.pick chooser + [] let tryPick chooser = raiseOrReturn >> Seq.tryPick chooser + [] let choose chooser = raiseOrReturn >> Seq.choose chooser >> ofSeq + [] let partition predicate list = check list let res1 = builderWith list.Length @@ -235,60 +314,98 @@ module FlatList = if predicate x then res1.Add(x) else res2.Add(x) ofBuilder res1, ofBuilder res2 + [] + let rec iterList (list:FlatList<_>) index predicate indexPredicate indexTransform = + if indexPredicate index then + if predicate list.[index] then + Some (index, list.[index]) + else iterList list (indexTransform index) predicate indexPredicate indexTransform + else None + + [] + let tryFindItem predicate direction list = + check 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) + iterList list startIndex predicate indexPredicate transform + + [] let find predicate = raiseOrReturn >> Seq.find predicate + [] let tryFind predicate = raiseOrReturn >> Seq.tryFind predicate + [] let findBack predicate = raiseOrReturn >> Seq.findBack predicate + [] let tryFindBack predicate = raiseOrReturn >> Seq.tryFindBack predicate + [] let findIndex predicate = raiseOrReturn >> Seq.findIndex predicate + [] let findIndexBack predicate = raiseOrReturn >> Seq.findIndexBack predicate + [] let tryFindIndex predicate = raiseOrReturn >> Seq.tryFindIndex predicate + [] let tryFindIndexBack predicate = raiseOrReturn >> Seq.tryFindIndexBack predicate + [] let fold folder (state: 'state) = raiseOrReturn >> Seq.fold folder state + [] let scan folder (state: 'state) = raiseOrReturn >> Seq.scan folder state >> ofSeq + [] 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 Seq.foldBack folder list state + [] let scanBack folder (list:FlatList<'a>) (state:'state) = check list Seq.scanBack folder list state |> ofSeq + [] let unfold (generator: 'state -> ('a * 'state) option) state = Seq.unfold generator state |> ofSeq + [] let reduce reduction = raiseOrReturn >> Seq.reduce reduction + [] let reduceBack reduction = raiseOrReturn >> Seq.reduceBack reduction + [] let mapFold mapping (state:'State) (list:FlatList<'T>) = check list let (items, s) = Seq.mapFold mapping state list ofSeq items, s + [] let mapFoldBack mapping (list:FlatList<'T>) (state:'State) = check list let (i, s) = Seq.mapFoldBack mapping list state ofSeq i, s + [] let zip (left:FlatList<_>) (right:FlatList<_>) = check left; check right Seq.zip left right |> ofSeq + [] let zip3 (left:FlatList<_>) (middle:FlatList<_>) (right:FlatList<_>) = check left; check middle; check right Seq.zip3 left middle right |> ofSeq + [] let unzip list = let left = builderWithLengthOf list let right = builderWithLengthOf list @@ -297,6 +414,7 @@ module FlatList = right.Add <| snd item ofBuilder left, ofBuilder right + [] let unzip3 list = let left = builderWithLengthOf list let right = builderWithLengthOf list @@ -307,14 +425,16 @@ module FlatList = right.Add <| thd3 item ofBuilder left, ofBuilder middle, ofBuilder right + [] let windowed windowSize = raiseOrReturn >> Seq.windowed windowSize >> Seq.map ofSeq >> ofSeq + [] let fill target targetIndex count value = mapi (fun i a -> if targetIndex <= i && i < targetIndex + count then value else a) target - |> ofSeq ////////// Based on other operations ////////// + [] let take count list = removeRange count (length list - count) list let inline private lengthWhile predicate list = @@ -323,83 +443,131 @@ 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 = skip 1 list + [] let tryTail list = if isEmpty list then None else Some <| tail list + [] let create = initWithValue + [] let replicate item = item |> flip initWithValue + [] 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 inline sum ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) = list |> raiseOrReturn |> reduce (+) + [] let inline sumBy projection ( list:FlatList< ^T > when ^T : (static member (+) : ^T * ^T -> ^T) and ^T : (static member Zero : ^T) ) = list |> raiseOrReturn |> 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) ) = list |> raiseOrReturn |> 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 |> raiseOrReturn |> applyOverFuncs LanguagePrimitives.DivideByInt ((map projection) >> sum) length - let maxBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> Seq.map projection |> Seq.reduce max - let minBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> Seq.map projection |> Seq.reduce min + 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 |> raiseOrReturn |> reduce (minMaxReduction projection (>)) + + [] + let minBy projection (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce (minMaxReduction projection (<)) + + [] let max (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce max + [] let min (list:FlatList<'a> when 'a : comparison) = list |> raiseOrReturn |> reduce 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 |> raiseOrReturn |> Seq.rev |> ofSeq + [] let transpose (list:FlatList<_>) = list |> raiseOrReturn |> Seq.transpose |> Seq.map ofSeq |> ofSeq + [] let permute indexMap (list:FlatList<_>) = list |> raiseOrReturn |> Seq.permute indexMap |> ofSeq + [] let pairwise (list:FlatList<_>) = list |> raiseOrReturn |> Seq.pairwise |> ofSeq + [] let except itemsToExclude (list:FlatList<_>) = list |> raiseOrReturn |> Seq.except itemsToExclude |> ofSeq + [] let splitInto count (list:FlatList<_>) = list |> raiseOrReturn |> Seq.splitInto count |> Seq.map ofSeq |> ofSeq + [] let chunkBySize chunkSize (list:FlatList<_>) = list |> raiseOrReturn |> Seq.chunkBySize chunkSize |> Seq.map ofSeq |> ofSeq + [] let allPairs (left:FlatList<'a>) (right:FlatList<'b>) = Seq.allPairs (raiseOrReturn left) (raiseOrReturn right) |> ofSeq ////////// From c0d8313d464f85238aee81d74c6d197db593cdbb Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Tue, 22 Jun 2021 08:22:35 +0300 Subject: [PATCH 20/24] fixes upon review --- src/FSharp.Collections.Immutable/flat-list.fs | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 1c7c3cb..2e974f4 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -224,10 +224,10 @@ module FlatList = [] let indexed list = list |> raiseOrReturn |> Seq.indexed |> ofSeq - [] + [] let iter action = raiseOrReturn >> Seq.iter action - [] + [] let iter2 action list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 @@ -253,22 +253,22 @@ module FlatList = Seq.map3 mapping list1 list2 list3 |> ofSeq - [] + [] let mapi2 mapping list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 Seq.mapi2 mapping list1 list2 |> ofSeq - [] + [] let iteri action = raiseOrReturn >> Seq.iteri action - [] + [] let iteri2 action list1 list2 = checkNotDefault (nameof list1) list1 checkNotDefault (nameof list2) list2 Seq.iteri2 action list1 list2 - [] + [] let mapi mapping = raiseOrReturn >> Seq.mapi mapping >> ofSeq [] @@ -314,12 +314,11 @@ module FlatList = if predicate x then res1.Add(x) else res2.Add(x) ofBuilder res1, ofBuilder res2 - [] - let rec iterList (list:FlatList<_>) index predicate indexPredicate indexTransform = + let rec private tryFindWithCustomStride (list:FlatList<_>) index predicate indexPredicate indexTransform = if indexPredicate index then if predicate list.[index] then Some (index, list.[index]) - else iterList list (indexTransform index) predicate indexPredicate indexTransform + else tryFindWithCustomStride list (indexTransform index) predicate indexPredicate indexTransform else None [] @@ -327,12 +326,12 @@ module FlatList = check 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) - iterList list startIndex predicate indexPredicate transform + let transform = if direction then ((+) 1) else ((+) -1) // because section is not available + tryFindWithCustomStride list startIndex predicate indexPredicate transform [] let find predicate = raiseOrReturn >> Seq.find predicate - [] + [] let tryFind predicate = raiseOrReturn >> Seq.tryFind predicate [] let findBack predicate = raiseOrReturn >> Seq.findBack predicate @@ -553,7 +552,7 @@ module FlatList = [] let exactlyOne (list:FlatList<_>) = Seq.exactlyOne list - [] + [] let rev (list:FlatList<_>) = list |> raiseOrReturn |> Seq.rev |> ofSeq [] let transpose (list:FlatList<_>) = list |> raiseOrReturn |> Seq.transpose |> Seq.map ofSeq |> ofSeq From 64bd4eadb9c049c0c1d9669f0751b3d1bab70e3a Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Tue, 22 Jun 2021 10:14:13 +0300 Subject: [PATCH 21/24] tests pack - parameter validations - operations - wip --- .../flat-list-tests.fs | 174 +++++++++++------- src/FSharp.Collections.Immutable/flat-list.fs | 2 +- 2 files changed, 112 insertions(+), 64 deletions(-) diff --git a/FSharp.Collections.Immutable.Tests/flat-list-tests.fs b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs index c220930..8250218 100644 --- a/FSharp.Collections.Immutable.Tests/flat-list-tests.fs +++ b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs @@ -1,7 +1,6 @@ module FSharp.Collections.Immutable.Tests open NUnit.Framework -open Fuchu open FSharp.Collections.Immutable let nullList<'a> = new FlatList<'a>() @@ -18,71 +17,120 @@ let throwsOn item func = let doesNotThrowOn item func = fun () -> Assert.DoesNotThrow (fun () -> func item) -let throwsOnlyOnNullLists func = [ - testCase "throws on null list" ((func >> ignore) |> throwsOn nullList) - testCase "does not throw on empty list" ((func >> ignore) |> doesNotThrowOn emptyList) - testCase "does not throw on non-empty list" ((func >> ignore) |> doesNotThrowOn list0to9) +let testCase name category (code:unit->unit) = ((TestCaseData code).SetName (category + " - " + name)).SetCategory category + +let acceptsAllValues func category = [ + testCase "throws on null list" category ((func >> ignore) |> doesNotThrowOn nullList) + testCase "does not throw on empty list" category ((func >> ignore) |> doesNotThrowOn emptyList) + testCase "does not throw on non-empty list" category ((func >> ignore) |> doesNotThrowOn list0to9) +] + +let throwsOnlyOnNullLists func category = [ + testCase "throws on null list" category ((func >> ignore) |> throwsOn nullList) + testCase "does not throw on empty list" category ((func >> ignore) |> doesNotThrowOn emptyList) + testCase "does not throw on non-empty list" category ((func >> ignore) |> doesNotThrowOn list0to9) ] -let doesNotThrowOnlyOnFilledList func = [ - testCase "throws on null list" ((func >> ignore) |> throwsOn nullList) - testCase "throws on empty list" ((func >> ignore) |> throwsOn emptyList) - testCase "does not throw on non-empty list" ((func >> ignore) |> doesNotThrowOn list0to9) +let doesNotThrowOnlyOnFilledList func category = [ + testCase "throws on null list" category ((func >> ignore) |> throwsOn nullList) + testCase "throws on empty list" category ((func >> ignore) |> throwsOn emptyList) + testCase "does not throw on non-empty list" category ((func >> ignore) |> doesNotThrowOn list0to9) ] -[] -let flatListTests = - testList "FlatList" [ - testList "toArray" (FlatList.toArray |> throwsOnlyOnNullLists) - testList "toList" (FlatList.toList |> throwsOnlyOnNullLists) - testList "toSeq" (FlatList.toSeq |> throwsOnlyOnNullLists) - testList "length" (FlatList.length |> throwsOnlyOnNullLists) - testList "append (first arg)" ((fun a -> FlatList.append a emptyList |> ignore) |> throwsOnlyOnNullLists) - testList "append (second arg)" ((FlatList.append emptyList) |> throwsOnlyOnNullLists) - testList "indexFromWith" ((FlatList.indexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> throwsOnlyOnNullLists) - testList "indexFrom" ((FlatList.indexFrom 0 1) |> throwsOnlyOnNullLists) - testList "indexWith" ((FlatList.indexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> throwsOnlyOnNullLists) - testList "index" ((FlatList.index 1) |> throwsOnlyOnNullLists) - testList "removeAllWith" ((FlatList.removeAllWith LanguagePrimitives.FastGenericEqualityComparer emptyList) |> throwsOnlyOnNullLists) - testList "removeAll" ((FlatList.removeAll emptyList) |> throwsOnlyOnNullLists) - testList "filter" ((FlatList.filter noopPredicate) |> throwsOnlyOnNullLists) - testList "where" ((FlatList.where noopPredicate) |> throwsOnlyOnNullLists) - testList "sortWithComparer" ((FlatList.sortWithComparer LanguagePrimitives.FastGenericComparer) |> throwsOnlyOnNullLists) - testList "sortWith" ((FlatList.sortWith LanguagePrimitives.GenericComparison) |> throwsOnlyOnNullLists) - testList "sort" (FlatList.sort |> throwsOnlyOnNullLists) - testList "map" (FlatList.map noopPredicate |> throwsOnlyOnNullLists) - testList "countBy" (FlatList.countBy noopPredicate |> throwsOnlyOnNullLists) - testList "indexed" (FlatList.indexed |> throwsOnlyOnNullLists) - testList "iter" (FlatList.iter ignore |> throwsOnlyOnNullLists) - testList "iter2 (first arg)" ((fun a -> FlatList.iter2 ignore2 a emptyList) |> throwsOnlyOnNullLists) - testList "iter2 (second arg)" ((FlatList.iter2 ignore2 emptyList) |> throwsOnlyOnNullLists) - testList "distinct" (FlatList.distinct |> throwsOnlyOnNullLists) - testList "distinctBy" (FlatList.distinctBy noopPredicate |> throwsOnlyOnNullLists) - testList "map2 (first arg)" ((fun a -> FlatList.map2 ignore2 a emptyList) |> throwsOnlyOnNullLists) - testList "map2 (second arg)" ((FlatList.map2 ignore2 emptyList) |> throwsOnlyOnNullLists) - testList "map3 (first arg)" ((fun a -> FlatList.map3 ignore3 a emptyList emptyList) |> throwsOnlyOnNullLists) - testList "map3 (second arg)" ((fun a -> FlatList.map3 ignore3 a emptyList emptyList) |> throwsOnlyOnNullLists) - testList "map3 (third arg)" ((FlatList.map3 ignore3 emptyList emptyList) |> throwsOnlyOnNullLists) - testList "mapi2 (first arg)" ((fun a -> FlatList.mapi2 ignore3 a emptyList) |> throwsOnlyOnNullLists) - testList "mapi2 (second arg)" ((FlatList.mapi2 ignore3 emptyList) |> throwsOnlyOnNullLists) - testList "iteri" ((FlatList.iteri ignore2) |> throwsOnlyOnNullLists) - testList "iteri2 (first arg)" ((fun a -> FlatList.iteri2 ignore3 a emptyList) |> throwsOnlyOnNullLists) - testList "iteri2 (second arg)" ((FlatList.iteri2 ignore3 emptyList) |> throwsOnlyOnNullLists) - testList "mapi" ((FlatList.mapi ignore2) |> throwsOnlyOnNullLists) - - testList "item" ((FlatList.item 0) |> doesNotThrowOnlyOnFilledList) - testList "indexRangeWith" ((FlatList.indexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList) - testList "indexRange" ((FlatList.indexRange 0 1 1) |> doesNotThrowOnlyOnFilledList) - testList "lastIndexRangeWith" ((FlatList.lastIndexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList) - testList "lastIndexRange" ((FlatList.lastIndexRange 0 1 1) |> doesNotThrowOnlyOnFilledList) - testList "lastIndexFromWith" ((FlatList.lastIndexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> doesNotThrowOnlyOnFilledList) - testList "lastIndexFrom" ((FlatList.lastIndexFrom 0 1) |> doesNotThrowOnlyOnFilledList) - testList "lastIndexWith" ((FlatList.lastIndexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> doesNotThrowOnlyOnFilledList) - testList "lastIndex" ((FlatList.lastIndex 1) |> doesNotThrowOnlyOnFilledList) - testList "removeRange" ((FlatList.removeRange 0 1) |> doesNotThrowOnlyOnFilledList) - testList "blit" ((fun a -> FlatList.blit a 0 [|10;11;12|] 0 1) |> doesNotThrowOnlyOnFilledList) - testList "sortRangeWithComparer" ((FlatList.sortRangeWithComparer LanguagePrimitives.FastGenericComparer 0 1) |> doesNotThrowOnlyOnFilledList) - testList "sortRangeWith" ((FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 1) |> doesNotThrowOnlyOnFilledList) - testList "sortRange" ((FlatList.sortRange 0 1) |> doesNotThrowOnlyOnFilledList) +let expectType<'a> value = Assert.IsInstanceOf<'a> value +let expectValue expected actual = Assert.AreEqual (expected, actual) +let expectToBeEquivalentTo expected actual = CollectionAssert.AreEquivalent (expected, actual) + +let appliedTo value func = fun () -> func value +let produces value func = + fun () -> + let a = func () + expectValue value a + +let producesEquivalentOf value func = + fun () -> + () |> func |> expectToBeEquivalentTo value +let throws func = fun () -> Assert.Catch (func >> ignore) |> ignore + +type FlatListFixture () = + static member validationCases = Seq.concat [ + "toSeq" |> (FlatList.toSeq |> throwsOnlyOnNullLists) + "toArray" |> (FlatList.toArray |> throwsOnlyOnNullLists) + "toList" |> (FlatList.toList |> throwsOnlyOnNullLists) + "length" |> (FlatList.length |> throwsOnlyOnNullLists) + "append (first arg)" |> ((fun a -> FlatList.append a emptyList |> ignore) |> throwsOnlyOnNullLists) + "append (second arg)" |> ((FlatList.append emptyList) |> throwsOnlyOnNullLists) + "indexFromWith" |> ((FlatList.indexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> throwsOnlyOnNullLists) + "indexFrom" |> ((FlatList.indexFrom 0 1) |> throwsOnlyOnNullLists) + "indexWith" |> ((FlatList.indexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> throwsOnlyOnNullLists) + "index" |> ((FlatList.index 1) |> throwsOnlyOnNullLists) + "removeAllWith" |> ((FlatList.removeAllWith LanguagePrimitives.FastGenericEqualityComparer emptyList) |> throwsOnlyOnNullLists) + "removeAll" |> ((FlatList.removeAll emptyList) |> throwsOnlyOnNullLists) + "filter" |> ((FlatList.filter noopPredicate) |> throwsOnlyOnNullLists) + "where" |> ((FlatList.where noopPredicate) |> throwsOnlyOnNullLists) + "sortWithComparer" |> ((FlatList.sortWithComparer LanguagePrimitives.FastGenericComparer) |> throwsOnlyOnNullLists) + "sortWith" |> ((FlatList.sortWith LanguagePrimitives.GenericComparison) |> throwsOnlyOnNullLists) + "sort" |> (FlatList.sort |> throwsOnlyOnNullLists) + "map" |> (FlatList.map noopPredicate |> throwsOnlyOnNullLists) + "countBy" |> (FlatList.countBy noopPredicate |> throwsOnlyOnNullLists) + "indexed" |> (FlatList.indexed |> throwsOnlyOnNullLists) + "iter" |> (FlatList.iter ignore |> throwsOnlyOnNullLists) + "iter2 (first arg)" |> ((fun a -> FlatList.iter2 ignore2 a emptyList) |> throwsOnlyOnNullLists) + "iter2 (second arg)" |> ((FlatList.iter2 ignore2 emptyList) |> throwsOnlyOnNullLists) + "distinct" |> (FlatList.distinct |> throwsOnlyOnNullLists) + "distinctBy" |> (FlatList.distinctBy noopPredicate |> throwsOnlyOnNullLists) + "map2 (first arg)" |> ((fun a -> FlatList.map2 ignore2 a emptyList) |> throwsOnlyOnNullLists) + "map2 (second arg)" |> ((FlatList.map2 ignore2 emptyList) |> throwsOnlyOnNullLists) + "map3 (first arg)" |> ((fun a -> FlatList.map3 ignore3 a emptyList emptyList) |> throwsOnlyOnNullLists) + "map3 (second arg)" |> ((fun a -> FlatList.map3 ignore3 a emptyList emptyList) |> throwsOnlyOnNullLists) + "map3 (third arg)" |> ((FlatList.map3 ignore3 emptyList emptyList) |> throwsOnlyOnNullLists) + "mapi2 (first arg)" |> ((fun a -> FlatList.mapi2 ignore3 a emptyList) |> throwsOnlyOnNullLists) + "mapi2 (second arg)" |> ((FlatList.mapi2 ignore3 emptyList) |> throwsOnlyOnNullLists) + "iteri" |> ((FlatList.iteri ignore2) |> throwsOnlyOnNullLists) + "iteri2 (first arg)" |> ((fun a -> FlatList.iteri2 ignore3 a emptyList) |> throwsOnlyOnNullLists) + "iteri2 (second arg)" |> ((FlatList.iteri2 ignore3 emptyList) |> throwsOnlyOnNullLists) + "mapi" |> ((FlatList.mapi ignore2) |> throwsOnlyOnNullLists) + + "item" |> ((FlatList.item 0) |> doesNotThrowOnlyOnFilledList) + "indexRangeWith" |> ((FlatList.indexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList) + "indexRange" |> ((FlatList.indexRange 0 1 1) |> doesNotThrowOnlyOnFilledList) + "lastIndexRangeWith" |> ((FlatList.lastIndexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList) + "lastIndexRange" |> ((FlatList.lastIndexRange 0 1 1) |> doesNotThrowOnlyOnFilledList) + "lastIndexFromWith" |> ((FlatList.lastIndexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> doesNotThrowOnlyOnFilledList) + "lastIndexFrom" |> ((FlatList.lastIndexFrom 0 1) |> doesNotThrowOnlyOnFilledList) + "lastIndexWith" |> ((FlatList.lastIndexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> doesNotThrowOnlyOnFilledList) + "lastIndex" |> ((FlatList.lastIndex 1) |> doesNotThrowOnlyOnFilledList) + "removeRange" |> ((FlatList.removeRange 0 1) |> doesNotThrowOnlyOnFilledList) + "blit" |> ((fun a -> FlatList.blit a 0 [|10;11;12|] 0 1) |> doesNotThrowOnlyOnFilledList) + "sortRangeWithComparer" |> ((FlatList.sortRangeWithComparer LanguagePrimitives.FastGenericComparer 0 1) |> doesNotThrowOnlyOnFilledList) + "sortRangeWith" |> ((FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 1) |> doesNotThrowOnlyOnFilledList) + "sortRange" |> ((FlatList.sortRange 0 1) |> doesNotThrowOnlyOnFilledList) + ] + + static member operationCases = [ + (FlatList.isEmpty |> appliedTo emptyList |> produces true) |> testCase "empty list is" "isEmpty" + (FlatList.isEmpty |> appliedTo list0to9 |> produces false) |> testCase "non-empty list is not" "isEmpty" + (FlatList.isDefault |> appliedTo nullList |> produces true) |> testCase "null list is" "isDefault" + (FlatList.isDefault |> appliedTo emptyList |> produces false) |> testCase "empty list is not" "isDefault" + (FlatList.isDefault |> appliedTo list0to9 |> produces false) |> testCase "non-empty list is not" "isDefault" + (FlatList.isDefaultOrEmpty |> appliedTo nullList |> produces true) |> testCase "null list is" "isDefaultOrEmpty" + (FlatList.isDefaultOrEmpty |> appliedTo emptyList |> produces true) |> testCase "empty list is" "isDefaultOrEmpty" + (FlatList.isDefaultOrEmpty |> appliedTo list0to9 |> produces false) |> testCase "non-empty list is not" "isDefaultOrEmpty" + (FlatList.length |> appliedTo emptyList |> produces 0) |> testCase "for empty list is 0" "length" + (FlatList.length |> appliedTo list0to9 |> produces 10) |> testCase "for non-empty list is .length" "length" + (FlatList.item 0 |> appliedTo emptyList |> throws) |> testCase "throws for empty list" "item" + (FlatList.item 0 |> appliedTo list0to9 |> produces 0) |> testCase "[0] for non-empty list equals to [0]" "item" + (FlatList.item 5 |> appliedTo list0to9 |> produces 5) |> testCase "[5] for non-empty list equals to [5]" "item" + (FlatList.item -1 |> appliedTo list0to9 |> throws) |> testCase "[-1] for non-empty list throws" "item" + (FlatList.item 25 |> appliedTo list0to9 |> throws) |> testCase "[out of bounds] for non-empty list throws" "item" + (FlatList.append emptyList |> appliedTo list0to9 |> producesEquivalentOf [0..9]) |> testCase "empty to non-empty" "append" + (FlatList.append list0to9 |> appliedTo emptyList |> producesEquivalentOf [0..9]) |> testCase "non-empty to empty" "append" + (FlatList.append list0to9 |> appliedTo list0to9 |> producesEquivalentOf (List.append [0..9] [0..9])) |> testCase "non-empty to non-empty" "append" ] + + [] + member this.testParameterValidation (code:unit->unit) = code () + + [] + member this.testOperationResult (code:unit->unit) = code () diff --git a/src/FSharp.Collections.Immutable/flat-list.fs b/src/FSharp.Collections.Immutable/flat-list.fs index 2e974f4..6739494 100644 --- a/src/FSharp.Collections.Immutable/flat-list.fs +++ b/src/FSharp.Collections.Immutable/flat-list.fs @@ -39,7 +39,7 @@ module FlatList = let inline ofList (source: _ list) = FlatListFactory.CreateRange source [] - let inline toSeq (flatList: FlatList<_>) = flatList :> seq<_> + let inline toSeq (flatList: FlatList<_>) = check flatList; flatList :> seq<_> [] let inline toArray (list : FlatList<_>) = check list; Seq.toArray list [] From 7be62fe881bf6dfdab2596609c843094c63753a3 Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Mon, 28 Jun 2021 12:39:49 +0300 Subject: [PATCH 22/24] fix tests (remove dependency on same instance of lists), tests for several functions, refactoring --- .../FSharp.Collections.Immutable.Tests.fsproj | 6 +- FSharp.Collections.Immutable.Tests/Program.fs | 5 - .../flat-list-tests.fs | 216 ++++++++---------- .../test-helpers.fs | 17 ++ 4 files changed, 117 insertions(+), 127 deletions(-) delete mode 100644 FSharp.Collections.Immutable.Tests/Program.fs create mode 100644 FSharp.Collections.Immutable.Tests/test-helpers.fs diff --git a/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj b/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj index ad19202..3401609 100644 --- a/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj +++ b/FSharp.Collections.Immutable.Tests/FSharp.Collections.Immutable.Tests.fsproj @@ -8,15 +8,15 @@ - - + + + - diff --git a/FSharp.Collections.Immutable.Tests/Program.fs b/FSharp.Collections.Immutable.Tests/Program.fs deleted file mode 100644 index 657e7d5..0000000 --- a/FSharp.Collections.Immutable.Tests/Program.fs +++ /dev/null @@ -1,5 +0,0 @@ -open Fuchu - -module Program = - let [] main args = defaultMainThisAssembly args - diff --git a/FSharp.Collections.Immutable.Tests/flat-list-tests.fs b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs index 8250218..af4011c 100644 --- a/FSharp.Collections.Immutable.Tests/flat-list-tests.fs +++ b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs @@ -1,132 +1,110 @@ -module FSharp.Collections.Immutable.Tests +namespace FSharp.Collections.Immutable.Tests open NUnit.Framework open FSharp.Collections.Immutable -let nullList<'a> = new FlatList<'a>() -let emptyList<'a> = FlatList<'a>.Empty -let list0to9 = FlatList.init 10 id -let noopPredicate _ = true -let ignore2 _ _ = () -let ignore3 _ _ _ = () - -let throwsOn item func = - fun () -> - Assert.Catch (fun () -> func item) - |> ignore - -let doesNotThrowOn item func = fun () -> Assert.DoesNotThrow (fun () -> func item) - -let testCase name category (code:unit->unit) = ((TestCaseData code).SetName (category + " - " + name)).SetCategory category - -let acceptsAllValues func category = [ - testCase "throws on null list" category ((func >> ignore) |> doesNotThrowOn nullList) - testCase "does not throw on empty list" category ((func >> ignore) |> doesNotThrowOn emptyList) - testCase "does not throw on non-empty list" category ((func >> ignore) |> doesNotThrowOn list0to9) -] - -let throwsOnlyOnNullLists func category = [ - testCase "throws on null list" category ((func >> ignore) |> throwsOn nullList) - testCase "does not throw on empty list" category ((func >> ignore) |> doesNotThrowOn emptyList) - testCase "does not throw on non-empty list" category ((func >> ignore) |> doesNotThrowOn list0to9) -] - -let doesNotThrowOnlyOnFilledList func category = [ - testCase "throws on null list" category ((func >> ignore) |> throwsOn nullList) - testCase "throws on empty list" category ((func >> ignore) |> throwsOn emptyList) - testCase "does not throw on non-empty list" category ((func >> ignore) |> doesNotThrowOn list0to9) -] - -let expectType<'a> value = Assert.IsInstanceOf<'a> value -let expectValue expected actual = Assert.AreEqual (expected, actual) -let expectToBeEquivalentTo expected actual = CollectionAssert.AreEquivalent (expected, actual) - -let appliedTo value func = fun () -> func value -let produces value func = - fun () -> - let a = func () - expectValue value a - -let producesEquivalentOf value func = - fun () -> - () |> func |> expectToBeEquivalentTo value - -let throws func = fun () -> Assert.Catch (func >> ignore) |> ignore +[] +module FlatListTestsUtils = + let nullList<'a> () = new FlatList<'a>() + let emptyList<'a> () = FlatList<'a>.Empty + let list0to9 () = FlatList.init 10 id + let throwsOnlyOnNullLists category func = [ + (func |> appliedTo (nullList ()) |> throws) |> testCase "throws on null list" category + (func |> appliedTo (emptyList ()) |> doesNotThrow) |> testCase "does not throw on empty list" category + (func |> appliedTo (list0to9 ()) |> doesNotThrow) |> testCase "does not throw on non-empty list" category + ] + let doesNotThrowOnlyOnFilledList category func = [ + (func |> appliedTo (nullList ()) |> throws) |> testCase "throws on null list" category + (func |> appliedTo (emptyList ()) |> throws) |> testCase "throws on empty list" category + (func |> appliedTo (list0to9 ()) |> doesNotThrow) |> testCase "does not throw on non-empty list" category + ] type FlatListFixture () = static member validationCases = Seq.concat [ - "toSeq" |> (FlatList.toSeq |> throwsOnlyOnNullLists) - "toArray" |> (FlatList.toArray |> throwsOnlyOnNullLists) - "toList" |> (FlatList.toList |> throwsOnlyOnNullLists) - "length" |> (FlatList.length |> throwsOnlyOnNullLists) - "append (first arg)" |> ((fun a -> FlatList.append a emptyList |> ignore) |> throwsOnlyOnNullLists) - "append (second arg)" |> ((FlatList.append emptyList) |> throwsOnlyOnNullLists) - "indexFromWith" |> ((FlatList.indexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> throwsOnlyOnNullLists) - "indexFrom" |> ((FlatList.indexFrom 0 1) |> throwsOnlyOnNullLists) - "indexWith" |> ((FlatList.indexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> throwsOnlyOnNullLists) - "index" |> ((FlatList.index 1) |> throwsOnlyOnNullLists) - "removeAllWith" |> ((FlatList.removeAllWith LanguagePrimitives.FastGenericEqualityComparer emptyList) |> throwsOnlyOnNullLists) - "removeAll" |> ((FlatList.removeAll emptyList) |> throwsOnlyOnNullLists) - "filter" |> ((FlatList.filter noopPredicate) |> throwsOnlyOnNullLists) - "where" |> ((FlatList.where noopPredicate) |> throwsOnlyOnNullLists) - "sortWithComparer" |> ((FlatList.sortWithComparer LanguagePrimitives.FastGenericComparer) |> throwsOnlyOnNullLists) - "sortWith" |> ((FlatList.sortWith LanguagePrimitives.GenericComparison) |> throwsOnlyOnNullLists) - "sort" |> (FlatList.sort |> throwsOnlyOnNullLists) - "map" |> (FlatList.map noopPredicate |> throwsOnlyOnNullLists) - "countBy" |> (FlatList.countBy noopPredicate |> throwsOnlyOnNullLists) - "indexed" |> (FlatList.indexed |> throwsOnlyOnNullLists) - "iter" |> (FlatList.iter ignore |> throwsOnlyOnNullLists) - "iter2 (first arg)" |> ((fun a -> FlatList.iter2 ignore2 a emptyList) |> throwsOnlyOnNullLists) - "iter2 (second arg)" |> ((FlatList.iter2 ignore2 emptyList) |> throwsOnlyOnNullLists) - "distinct" |> (FlatList.distinct |> throwsOnlyOnNullLists) - "distinctBy" |> (FlatList.distinctBy noopPredicate |> throwsOnlyOnNullLists) - "map2 (first arg)" |> ((fun a -> FlatList.map2 ignore2 a emptyList) |> throwsOnlyOnNullLists) - "map2 (second arg)" |> ((FlatList.map2 ignore2 emptyList) |> throwsOnlyOnNullLists) - "map3 (first arg)" |> ((fun a -> FlatList.map3 ignore3 a emptyList emptyList) |> throwsOnlyOnNullLists) - "map3 (second arg)" |> ((fun a -> FlatList.map3 ignore3 a emptyList emptyList) |> throwsOnlyOnNullLists) - "map3 (third arg)" |> ((FlatList.map3 ignore3 emptyList emptyList) |> throwsOnlyOnNullLists) - "mapi2 (first arg)" |> ((fun a -> FlatList.mapi2 ignore3 a emptyList) |> throwsOnlyOnNullLists) - "mapi2 (second arg)" |> ((FlatList.mapi2 ignore3 emptyList) |> throwsOnlyOnNullLists) - "iteri" |> ((FlatList.iteri ignore2) |> throwsOnlyOnNullLists) - "iteri2 (first arg)" |> ((fun a -> FlatList.iteri2 ignore3 a emptyList) |> throwsOnlyOnNullLists) - "iteri2 (second arg)" |> ((FlatList.iteri2 ignore3 emptyList) |> throwsOnlyOnNullLists) - "mapi" |> ((FlatList.mapi ignore2) |> throwsOnlyOnNullLists) - - "item" |> ((FlatList.item 0) |> doesNotThrowOnlyOnFilledList) - "indexRangeWith" |> ((FlatList.indexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList) - "indexRange" |> ((FlatList.indexRange 0 1 1) |> doesNotThrowOnlyOnFilledList) - "lastIndexRangeWith" |> ((FlatList.lastIndexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList) - "lastIndexRange" |> ((FlatList.lastIndexRange 0 1 1) |> doesNotThrowOnlyOnFilledList) - "lastIndexFromWith" |> ((FlatList.lastIndexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> doesNotThrowOnlyOnFilledList) - "lastIndexFrom" |> ((FlatList.lastIndexFrom 0 1) |> doesNotThrowOnlyOnFilledList) - "lastIndexWith" |> ((FlatList.lastIndexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> doesNotThrowOnlyOnFilledList) - "lastIndex" |> ((FlatList.lastIndex 1) |> doesNotThrowOnlyOnFilledList) - "removeRange" |> ((FlatList.removeRange 0 1) |> doesNotThrowOnlyOnFilledList) - "blit" |> ((fun a -> FlatList.blit a 0 [|10;11;12|] 0 1) |> doesNotThrowOnlyOnFilledList) - "sortRangeWithComparer" |> ((FlatList.sortRangeWithComparer LanguagePrimitives.FastGenericComparer 0 1) |> doesNotThrowOnlyOnFilledList) - "sortRangeWith" |> ((FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 1) |> doesNotThrowOnlyOnFilledList) - "sortRange" |> ((FlatList.sortRange 0 1) |> doesNotThrowOnlyOnFilledList) + FlatList.toSeq |> throwsOnlyOnNullLists (nameof(FlatList.toSeq)) + FlatList.toArray |> throwsOnlyOnNullLists (nameof(FlatList.toArray)) + FlatList.toList |> throwsOnlyOnNullLists (nameof(FlatList.toList)) + FlatList.length |> throwsOnlyOnNullLists (nameof(FlatList.length)) + (flip FlatList.append <| emptyList ()) |> throwsOnlyOnNullLists (nameof(FlatList.append) + " (first arg)") + (FlatList.append <| emptyList ()) |> throwsOnlyOnNullLists (nameof(FlatList.append) + " (second arg)") + (FlatList.indexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> throwsOnlyOnNullLists (nameof(FlatList.indexFromWith)) + (FlatList.indexFrom 0 1) |> throwsOnlyOnNullLists (nameof(FlatList.indexFrom)) + (FlatList.indexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> throwsOnlyOnNullLists (nameof(FlatList.indexWith)) + (FlatList.index 1) |> throwsOnlyOnNullLists (nameof(FlatList.index)) + (FlatList.removeAllWith LanguagePrimitives.FastGenericEqualityComparer <| emptyList ()) |> throwsOnlyOnNullLists (nameof(FlatList.removeAllWith)) + (FlatList.removeAll <| emptyList ()) |> throwsOnlyOnNullLists (nameof(FlatList.removeAll)) + (FlatList.filter noopPredicate) |> throwsOnlyOnNullLists (nameof(FlatList.filter)) + (FlatList.where noopPredicate) |> throwsOnlyOnNullLists (nameof(FlatList.where)) + (FlatList.sortWithComparer LanguagePrimitives.FastGenericComparer) |> throwsOnlyOnNullLists (nameof(FlatList.sortWithComparer)) + (FlatList.sortWith LanguagePrimitives.GenericComparison) |> throwsOnlyOnNullLists (nameof(FlatList.sortWith)) + FlatList.sort |> throwsOnlyOnNullLists (nameof(FlatList.sort)) + FlatList.map noopPredicate |> throwsOnlyOnNullLists (nameof(FlatList.map)) + FlatList.countBy noopPredicate |> throwsOnlyOnNullLists (nameof(FlatList.countBy)) + FlatList.indexed |> throwsOnlyOnNullLists (nameof(FlatList.indexed)) + FlatList.iter ignore |> throwsOnlyOnNullLists (nameof(FlatList.iter)) + (flip (FlatList.iter2 ignore2) <| (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.iter2) + " (first arg)") + (FlatList.iter2 ignore2 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.iter2) + " (second arg)") + FlatList.distinct |> throwsOnlyOnNullLists (nameof(FlatList.distinct)) + FlatList.distinctBy noopPredicate |> throwsOnlyOnNullLists (nameof(FlatList.distinctBy)) + (flip (FlatList.map2 ignore2) <| (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map2) + " (first arg)") + (FlatList.map2 ignore2 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map2) + " (second arg)") + (fun a -> FlatList.map3 ignore3 a (emptyList ()) (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map3) + " (first arg)") + (fun a -> FlatList.map3 ignore3 (emptyList ()) a (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map3) + " (second arg)") + (FlatList.map3 ignore3 (emptyList ()) (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map3) + " (third arg)") + (flip (FlatList.mapi2 ignore3) <| (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.mapi2) + " (first arg)") + (FlatList.mapi2 ignore3 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.mapi2) + " (second arg)") + (FlatList.iteri ignore2) |> throwsOnlyOnNullLists (nameof(FlatList.iteri)) + (flip (FlatList.iteri2 ignore3) <| (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.iteri2) + " (first arg)") + (FlatList.iteri2 ignore3 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.iteri2) + " (second arg)") + (FlatList.mapi ignore2) |> throwsOnlyOnNullLists (nameof(FlatList.mapi)) + + (FlatList.item 0) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.item)) + (FlatList.indexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.indexRangeWith)) + (FlatList.indexRange 0 1 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.indexRange)) + (FlatList.lastIndexRangeWith LanguagePrimitives.FastGenericEqualityComparer 0 1 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndexRangeWith)) + (FlatList.lastIndexRange 0 1 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndexRange)) + (FlatList.lastIndexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndexFromWith)) + (FlatList.lastIndexFrom 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndexFrom)) + (FlatList.lastIndexWith LanguagePrimitives.FastGenericEqualityComparer 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndexWith)) + (FlatList.lastIndex 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.lastIndex)) + (FlatList.removeRange 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.removeRange)) + (fun a -> FlatList.blit a 0 [|10;11;12|] 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.blit)) + (FlatList.sortRangeWithComparer LanguagePrimitives.FastGenericComparer 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.sortRangeWithComparer)) + (FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.sortRangeWith)) + (FlatList.sortRange 0 1) |> doesNotThrowOnlyOnFilledList (nameof(FlatList.sortRange)) ] static member operationCases = [ - (FlatList.isEmpty |> appliedTo emptyList |> produces true) |> testCase "empty list is" "isEmpty" - (FlatList.isEmpty |> appliedTo list0to9 |> produces false) |> testCase "non-empty list is not" "isEmpty" - (FlatList.isDefault |> appliedTo nullList |> produces true) |> testCase "null list is" "isDefault" - (FlatList.isDefault |> appliedTo emptyList |> produces false) |> testCase "empty list is not" "isDefault" - (FlatList.isDefault |> appliedTo list0to9 |> produces false) |> testCase "non-empty list is not" "isDefault" - (FlatList.isDefaultOrEmpty |> appliedTo nullList |> produces true) |> testCase "null list is" "isDefaultOrEmpty" - (FlatList.isDefaultOrEmpty |> appliedTo emptyList |> produces true) |> testCase "empty list is" "isDefaultOrEmpty" - (FlatList.isDefaultOrEmpty |> appliedTo list0to9 |> produces false) |> testCase "non-empty list is not" "isDefaultOrEmpty" - (FlatList.length |> appliedTo emptyList |> produces 0) |> testCase "for empty list is 0" "length" - (FlatList.length |> appliedTo list0to9 |> produces 10) |> testCase "for non-empty list is .length" "length" - (FlatList.item 0 |> appliedTo emptyList |> throws) |> testCase "throws for empty list" "item" - (FlatList.item 0 |> appliedTo list0to9 |> produces 0) |> testCase "[0] for non-empty list equals to [0]" "item" - (FlatList.item 5 |> appliedTo list0to9 |> produces 5) |> testCase "[5] for non-empty list equals to [5]" "item" - (FlatList.item -1 |> appliedTo list0to9 |> throws) |> testCase "[-1] for non-empty list throws" "item" - (FlatList.item 25 |> appliedTo list0to9 |> throws) |> testCase "[out of bounds] for non-empty list throws" "item" - (FlatList.append emptyList |> appliedTo list0to9 |> producesEquivalentOf [0..9]) |> testCase "empty to non-empty" "append" - (FlatList.append list0to9 |> appliedTo emptyList |> producesEquivalentOf [0..9]) |> testCase "non-empty to empty" "append" - (FlatList.append list0to9 |> appliedTo list0to9 |> producesEquivalentOf (List.append [0..9] [0..9])) |> testCase "non-empty to non-empty" "append" + (FlatList.init 5 |> appliedTo (id) |> producesEquivalentOf [0..4]) |> testCase "for 5 elements yield valid list" (nameof(FlatList.init)) + (FlatList.init 0 |> appliedTo (id) |> producesEquivalentOf []) |> testCase "for 0 elements yields empty list" (nameof(FlatList.init)) + (FlatList.isEmpty |> appliedTo (emptyList ()) |> produces true) |> testCase "empty list is" (nameof(FlatList.isEmpty)) + (FlatList.isEmpty |> appliedTo (list0to9 ()) |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isEmpty)) + (FlatList.isDefault |> appliedTo (nullList ()) |> produces true) |> testCase "null list is" (nameof(FlatList.isDefault)) + (FlatList.isDefault |> appliedTo (emptyList ()) |> produces false) |> testCase "empty list is not" (nameof(FlatList.isDefault)) + (FlatList.isDefault |> appliedTo (list0to9 ()) |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isDefault)) + (FlatList.isDefaultOrEmpty |> appliedTo (nullList ()) |> produces true) |> testCase "null list is" (nameof(FlatList.isDefaultOrEmpty)) + (FlatList.isDefaultOrEmpty |> appliedTo (emptyList ()) |> produces true) |> testCase "empty list is" (nameof(FlatList.isDefaultOrEmpty)) + (FlatList.isDefaultOrEmpty |> appliedTo (list0to9 ()) |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isDefaultOrEmpty)) + (FlatList.length |> appliedTo (emptyList ()) |> produces 0) |> testCase "for empty list is 0" (nameof(FlatList.length)) + (FlatList.length |> appliedTo (list0to9 ()) |> produces 10) |> testCase "for non-empty list is .length" (nameof(FlatList.length)) + (FlatList.item 0 |> appliedTo (emptyList ()) |> throws) |> testCase "throws for empty list" (nameof(FlatList.item)) + (FlatList.item 0 |> appliedTo (list0to9 ()) |> produces 0) |> testCase "[0] for non-empty list equals to [0]" (nameof(FlatList.item)) + (FlatList.item 5 |> appliedTo (list0to9 ()) |> produces 5) |> testCase "[5] for non-empty list equals to [5]" (nameof(FlatList.item)) + (FlatList.item -1 |> appliedTo (list0to9 ()) |> throws) |> testCase "[-1] for non-empty list throws" (nameof(FlatList.item)) + (FlatList.item 25 |> appliedTo (list0to9 ()) |> throws) |> testCase "[out of bounds] for non-empty list throws" (nameof(FlatList.item)) + (FlatList.append (emptyList ()) |> appliedTo (list0to9 ()) |> producesEquivalentOf [0..9]) |> testCase "empty to non-empty" (nameof(FlatList.append)) + (FlatList.append (list0to9 ()) |> appliedTo (emptyList ()) |> producesEquivalentOf [0..9]) |> testCase "non-empty to empty" (nameof(FlatList.append)) + (FlatList.append (list0to9 ()) |> appliedTo (list0to9 ()) |> producesEquivalentOf (List.append [0..9] [0..9])) |> testCase "non-empty to non-empty" (nameof(FlatList.append)) + (FlatList.indexRangeWith HashIdentity.Structural 3 5 6 |> appliedTo (list0to9 ()) |> produces 6) |> testCase "returns index for valid args" (nameof(FlatList.indexRangeWith)) + (FlatList.indexRangeWith HashIdentity.Structural 3 15 6 |> appliedTo (list0to9 ()) |> throws) |> testCase "throws for invalid args (count)" (nameof(FlatList.indexRangeWith)) + (FlatList.indexRangeWith HashIdentity.Structural -2 5 6 |> appliedTo (list0to9 ()) |> throws) |> testCase "throws for invalid args (index)" (nameof(FlatList.indexRangeWith)) + (FlatList.indexRangeWith HashIdentity.Structural 3 5 0 |> appliedTo (list0to9 ()) |> produces -1) |> testCase "returns -1 for non-present item" (nameof(FlatList.indexRangeWith)) + (FlatList.removeAll ([] |> FlatList.ofList) |> appliedTo (list0to9 ()) |> producesEquivalentOf [0..9]) |> testCase "with empty list - returns source" (nameof(FlatList.removeAll)) + (FlatList.removeAll ([] |> FlatList.ofList) |> appliedTo (emptyList ()) |> producesEquivalentOf []) |> testCase "with empty list - returns source (even if empty)" (nameof(FlatList.removeAll)) + (FlatList.removeAll (list0to9 ()) |> appliedTo (emptyList ()) |> producesEquivalentOf []) |> testCase "takes no action for not present values" (nameof(FlatList.removeAll)) + (FlatList.removeAll (list0to9 ()) |> appliedTo (list0to9 ()) |> producesEquivalentOf []) |> testCase "removes all present items" (nameof(FlatList.removeAll)) + (FlatList.filter (fun i -> i < 4) |> appliedTo (list0to9 ()) |> producesEquivalentOf [0..3]) |> testCase "yields only valid items" (nameof(FlatList.filter)) + (FlatList.filter (fun i -> i > 100) |> appliedTo (list0to9 ()) |> producesEquivalentOf []) |> testCase "returns empty list if predicate matches no items" (nameof(FlatList.filter)) ] [] diff --git a/FSharp.Collections.Immutable.Tests/test-helpers.fs b/FSharp.Collections.Immutable.Tests/test-helpers.fs new file mode 100644 index 0000000..48b0973 --- /dev/null +++ b/FSharp.Collections.Immutable.Tests/test-helpers.fs @@ -0,0 +1,17 @@ +namespace FSharp.Collections.Immutable.Tests + +open FsUnit +open NUnit.Framework + +[] +module TestHelpers = + let appliedTo value func = fun () -> func value + let produces value func = fun () -> () |> func |> should equal value + let producesEquivalentOf value func = fun () -> () |> func |> should equivalent value + let throws func = fun () -> Assert.Catch (func >> ignore) |> ignore + let doesNotThrow func = fun () -> func () |> ignore |> should equal () + let noopPredicate _ = true + let ignore2 _ _ = () + let ignore3 _ _ _ = () + let testCase name category (code:unit->unit) = ((TestCaseData code).SetName (category + " - " + name)).SetCategory category + From d232b99046373c3cf346e8400f8aae4f74b048bd Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Mon, 12 Jul 2021 12:57:53 +0300 Subject: [PATCH 23/24] more tests --- .../flat-list-tests.fs | 110 +++++++++++------- .../test-helpers.fs | 5 +- 2 files changed, 75 insertions(+), 40 deletions(-) diff --git a/FSharp.Collections.Immutable.Tests/flat-list-tests.fs b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs index af4011c..177e1ef 100644 --- a/FSharp.Collections.Immutable.Tests/flat-list-tests.fs +++ b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs @@ -8,15 +8,21 @@ module FlatListTestsUtils = let nullList<'a> () = new FlatList<'a>() let emptyList<'a> () = FlatList<'a>.Empty let list0to9 () = FlatList.init 10 id + let list9to0 () = FlatList.init 10 ((-) 9) + let listOf count list = + let builder = FlatList.builderWith count + for i = 0 to count do + builder.Add list + FlatList.ofBuilder builder let throwsOnlyOnNullLists category func = [ - (func |> appliedTo (nullList ()) |> throws) |> testCase "throws on null list" category - (func |> appliedTo (emptyList ()) |> doesNotThrow) |> testCase "does not throw on empty list" category - (func |> appliedTo (list0to9 ()) |> doesNotThrow) |> testCase "does not throw on non-empty list" category + (func |> appliedToFunc nullList |> throws) |> testCase "throws on null list" category + (func |> appliedToFunc emptyList |> doesNotThrow) |> testCase "does not throw on empty list" category + (func |> appliedToFunc list0to9 |> doesNotThrow) |> testCase "does not throw on non-empty list" category ] let doesNotThrowOnlyOnFilledList category func = [ - (func |> appliedTo (nullList ()) |> throws) |> testCase "throws on null list" category - (func |> appliedTo (emptyList ()) |> throws) |> testCase "throws on empty list" category - (func |> appliedTo (list0to9 ()) |> doesNotThrow) |> testCase "does not throw on non-empty list" category + (func |> appliedToFunc nullList |> throws) |> testCase "throws on null list" category + (func |> appliedToFunc emptyList |> throws) |> testCase "throws on empty list" category + (func |> appliedToFunc list0to9 |> doesNotThrow) |> testCase "does not throw on non-empty list" category ] type FlatListFixture () = @@ -25,7 +31,7 @@ type FlatListFixture () = FlatList.toArray |> throwsOnlyOnNullLists (nameof(FlatList.toArray)) FlatList.toList |> throwsOnlyOnNullLists (nameof(FlatList.toList)) FlatList.length |> throwsOnlyOnNullLists (nameof(FlatList.length)) - (flip FlatList.append <| emptyList ()) |> throwsOnlyOnNullLists (nameof(FlatList.append) + " (first arg)") + (FlatList.append |> withSecondArgFrom emptyList) |> throwsOnlyOnNullLists (nameof(FlatList.append) + " (first arg)") (FlatList.append <| emptyList ()) |> throwsOnlyOnNullLists (nameof(FlatList.append) + " (second arg)") (FlatList.indexFromWith LanguagePrimitives.FastGenericEqualityComparer 0 1) |> throwsOnlyOnNullLists (nameof(FlatList.indexFromWith)) (FlatList.indexFrom 0 1) |> throwsOnlyOnNullLists (nameof(FlatList.indexFrom)) @@ -42,19 +48,19 @@ type FlatListFixture () = FlatList.countBy noopPredicate |> throwsOnlyOnNullLists (nameof(FlatList.countBy)) FlatList.indexed |> throwsOnlyOnNullLists (nameof(FlatList.indexed)) FlatList.iter ignore |> throwsOnlyOnNullLists (nameof(FlatList.iter)) - (flip (FlatList.iter2 ignore2) <| (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.iter2) + " (first arg)") + (FlatList.iter2 ignore2 |> withSecondArgFrom emptyList) |> throwsOnlyOnNullLists (nameof(FlatList.iter2) + " (first arg)") (FlatList.iter2 ignore2 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.iter2) + " (second arg)") FlatList.distinct |> throwsOnlyOnNullLists (nameof(FlatList.distinct)) FlatList.distinctBy noopPredicate |> throwsOnlyOnNullLists (nameof(FlatList.distinctBy)) - (flip (FlatList.map2 ignore2) <| (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map2) + " (first arg)") + (FlatList.map2 ignore2 |> withSecondArgFrom emptyList) |> throwsOnlyOnNullLists (nameof(FlatList.map2) + " (first arg)") (FlatList.map2 ignore2 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map2) + " (second arg)") (fun a -> FlatList.map3 ignore3 a (emptyList ()) (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map3) + " (first arg)") (fun a -> FlatList.map3 ignore3 (emptyList ()) a (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map3) + " (second arg)") (FlatList.map3 ignore3 (emptyList ()) (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.map3) + " (third arg)") - (flip (FlatList.mapi2 ignore3) <| (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.mapi2) + " (first arg)") + (FlatList.mapi2 ignore3 |> withSecondArgFrom emptyList) |> throwsOnlyOnNullLists (nameof(FlatList.mapi2) + " (first arg)") (FlatList.mapi2 ignore3 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.mapi2) + " (second arg)") (FlatList.iteri ignore2) |> throwsOnlyOnNullLists (nameof(FlatList.iteri)) - (flip (FlatList.iteri2 ignore3) <| (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.iteri2) + " (first arg)") + (FlatList.iteri2 ignore3 |> withSecondArgFrom emptyList) |> throwsOnlyOnNullLists (nameof(FlatList.iteri2) + " (first arg)") (FlatList.iteri2 ignore3 (emptyList ())) |> throwsOnlyOnNullLists (nameof(FlatList.iteri2) + " (second arg)") (FlatList.mapi ignore2) |> throwsOnlyOnNullLists (nameof(FlatList.mapi)) @@ -77,34 +83,60 @@ type FlatListFixture () = static member operationCases = [ (FlatList.init 5 |> appliedTo (id) |> producesEquivalentOf [0..4]) |> testCase "for 5 elements yield valid list" (nameof(FlatList.init)) (FlatList.init 0 |> appliedTo (id) |> producesEquivalentOf []) |> testCase "for 0 elements yields empty list" (nameof(FlatList.init)) - (FlatList.isEmpty |> appliedTo (emptyList ()) |> produces true) |> testCase "empty list is" (nameof(FlatList.isEmpty)) - (FlatList.isEmpty |> appliedTo (list0to9 ()) |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isEmpty)) - (FlatList.isDefault |> appliedTo (nullList ()) |> produces true) |> testCase "null list is" (nameof(FlatList.isDefault)) - (FlatList.isDefault |> appliedTo (emptyList ()) |> produces false) |> testCase "empty list is not" (nameof(FlatList.isDefault)) - (FlatList.isDefault |> appliedTo (list0to9 ()) |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isDefault)) - (FlatList.isDefaultOrEmpty |> appliedTo (nullList ()) |> produces true) |> testCase "null list is" (nameof(FlatList.isDefaultOrEmpty)) - (FlatList.isDefaultOrEmpty |> appliedTo (emptyList ()) |> produces true) |> testCase "empty list is" (nameof(FlatList.isDefaultOrEmpty)) - (FlatList.isDefaultOrEmpty |> appliedTo (list0to9 ()) |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isDefaultOrEmpty)) - (FlatList.length |> appliedTo (emptyList ()) |> produces 0) |> testCase "for empty list is 0" (nameof(FlatList.length)) - (FlatList.length |> appliedTo (list0to9 ()) |> produces 10) |> testCase "for non-empty list is .length" (nameof(FlatList.length)) - (FlatList.item 0 |> appliedTo (emptyList ()) |> throws) |> testCase "throws for empty list" (nameof(FlatList.item)) - (FlatList.item 0 |> appliedTo (list0to9 ()) |> produces 0) |> testCase "[0] for non-empty list equals to [0]" (nameof(FlatList.item)) - (FlatList.item 5 |> appliedTo (list0to9 ()) |> produces 5) |> testCase "[5] for non-empty list equals to [5]" (nameof(FlatList.item)) - (FlatList.item -1 |> appliedTo (list0to9 ()) |> throws) |> testCase "[-1] for non-empty list throws" (nameof(FlatList.item)) - (FlatList.item 25 |> appliedTo (list0to9 ()) |> throws) |> testCase "[out of bounds] for non-empty list throws" (nameof(FlatList.item)) - (FlatList.append (emptyList ()) |> appliedTo (list0to9 ()) |> producesEquivalentOf [0..9]) |> testCase "empty to non-empty" (nameof(FlatList.append)) - (FlatList.append (list0to9 ()) |> appliedTo (emptyList ()) |> producesEquivalentOf [0..9]) |> testCase "non-empty to empty" (nameof(FlatList.append)) - (FlatList.append (list0to9 ()) |> appliedTo (list0to9 ()) |> producesEquivalentOf (List.append [0..9] [0..9])) |> testCase "non-empty to non-empty" (nameof(FlatList.append)) - (FlatList.indexRangeWith HashIdentity.Structural 3 5 6 |> appliedTo (list0to9 ()) |> produces 6) |> testCase "returns index for valid args" (nameof(FlatList.indexRangeWith)) - (FlatList.indexRangeWith HashIdentity.Structural 3 15 6 |> appliedTo (list0to9 ()) |> throws) |> testCase "throws for invalid args (count)" (nameof(FlatList.indexRangeWith)) - (FlatList.indexRangeWith HashIdentity.Structural -2 5 6 |> appliedTo (list0to9 ()) |> throws) |> testCase "throws for invalid args (index)" (nameof(FlatList.indexRangeWith)) - (FlatList.indexRangeWith HashIdentity.Structural 3 5 0 |> appliedTo (list0to9 ()) |> produces -1) |> testCase "returns -1 for non-present item" (nameof(FlatList.indexRangeWith)) - (FlatList.removeAll ([] |> FlatList.ofList) |> appliedTo (list0to9 ()) |> producesEquivalentOf [0..9]) |> testCase "with empty list - returns source" (nameof(FlatList.removeAll)) - (FlatList.removeAll ([] |> FlatList.ofList) |> appliedTo (emptyList ()) |> producesEquivalentOf []) |> testCase "with empty list - returns source (even if empty)" (nameof(FlatList.removeAll)) - (FlatList.removeAll (list0to9 ()) |> appliedTo (emptyList ()) |> producesEquivalentOf []) |> testCase "takes no action for not present values" (nameof(FlatList.removeAll)) - (FlatList.removeAll (list0to9 ()) |> appliedTo (list0to9 ()) |> producesEquivalentOf []) |> testCase "removes all present items" (nameof(FlatList.removeAll)) - (FlatList.filter (fun i -> i < 4) |> appliedTo (list0to9 ()) |> producesEquivalentOf [0..3]) |> testCase "yields only valid items" (nameof(FlatList.filter)) - (FlatList.filter (fun i -> i > 100) |> appliedTo (list0to9 ()) |> producesEquivalentOf []) |> testCase "returns empty list if predicate matches no items" (nameof(FlatList.filter)) + (FlatList.isEmpty |> appliedToFunc emptyList |> produces true) |> testCase "empty list is" (nameof(FlatList.isEmpty)) + (FlatList.isEmpty |> appliedToFunc list0to9 |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isEmpty)) + (FlatList.isDefault |> appliedToFunc nullList |> produces true) |> testCase "null list is" (nameof(FlatList.isDefault)) + (FlatList.isDefault |> appliedToFunc emptyList |> produces false) |> testCase "empty list is not" (nameof(FlatList.isDefault)) + (FlatList.isDefault |> appliedToFunc list0to9 |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isDefault)) + (FlatList.isDefaultOrEmpty |> appliedToFunc nullList |> produces true) |> testCase "null list is" (nameof(FlatList.isDefaultOrEmpty)) + (FlatList.isDefaultOrEmpty |> appliedToFunc emptyList |> produces true) |> testCase "empty list is" (nameof(FlatList.isDefaultOrEmpty)) + (FlatList.isDefaultOrEmpty |> appliedToFunc list0to9 |> produces false) |> testCase "non-empty list is not" (nameof(FlatList.isDefaultOrEmpty)) + (FlatList.length |> appliedToFunc emptyList |> produces 0) |> testCase "for empty list is 0" (nameof(FlatList.length)) + (FlatList.length |> appliedToFunc list0to9 |> produces 10) |> testCase "for non-empty list is .length" (nameof(FlatList.length)) + (FlatList.item 0 |> appliedToFunc emptyList |> throws) |> testCase "throws for empty list" (nameof(FlatList.item)) + (FlatList.item 0 |> appliedToFunc list0to9 |> produces 0) |> testCase "[0] for non-empty list equals to [0]" (nameof(FlatList.item)) + (FlatList.item 5 |> appliedToFunc list0to9 |> produces 5) |> testCase "[5] for non-empty list equals to [5]" (nameof(FlatList.item)) + (FlatList.item -1 |> appliedToFunc list0to9 |> throws) |> testCase "[-1] for non-empty list throws" (nameof(FlatList.item)) + (FlatList.item 25 |> appliedToFunc list0to9 |> throws) |> testCase "[out of bounds] for non-empty list throws" (nameof(FlatList.item)) + (FlatList.append (emptyList ()) |> appliedToFunc list0to9 |> producesEquivalentOf [0..9]) |> testCase "empty to non-empty" (nameof(FlatList.append)) + (FlatList.append (list0to9 ()) |> appliedToFunc emptyList |> producesEquivalentOf [0..9]) |> testCase "non-empty to empty" (nameof(FlatList.append)) + (FlatList.append (list0to9 ()) |> appliedToFunc list0to9 |> producesEquivalentOf (List.append [0..9] [0..9])) |> testCase "non-empty to non-empty" (nameof(FlatList.append)) + (FlatList.indexRangeWith HashIdentity.Structural 3 5 6 |> appliedToFunc list0to9 |> produces 6) |> testCase "returns index for valid args" (nameof(FlatList.indexRangeWith)) + (FlatList.indexRangeWith HashIdentity.Structural 3 15 6 |> appliedToFunc list0to9 |> throws) |> testCase "throws for invalid args (count)" (nameof(FlatList.indexRangeWith)) + (FlatList.indexRangeWith HashIdentity.Structural -2 5 6 |> appliedToFunc list0to9 |> throws) |> testCase "throws for invalid args (index)" (nameof(FlatList.indexRangeWith)) + (FlatList.indexRangeWith HashIdentity.Structural 3 5 0 |> appliedToFunc list0to9 |> produces -1) |> testCase "returns -1 for non-present item" (nameof(FlatList.indexRangeWith)) + (FlatList.removeAll ([] |> FlatList.ofList) |> appliedToFunc list0to9 |> producesEquivalentOf [0..9]) |> testCase "with empty list - returns source" (nameof(FlatList.removeAll)) + (FlatList.removeAll ([] |> FlatList.ofList) |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "with empty list - returns source (even if empty)" (nameof(FlatList.removeAll)) + (FlatList.removeAll (list0to9 ()) |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "takes no action for not present values" (nameof(FlatList.removeAll)) + (FlatList.removeAll (list0to9 ()) |> appliedToFunc list0to9 |> producesEquivalentOf []) |> testCase "removes all present items" (nameof(FlatList.removeAll)) + (FlatList.filter ((>) 4) |> appliedToFunc list0to9 |> producesEquivalentOf [0..3]) |> testCase "yields only valid items" (nameof(FlatList.filter)) + (FlatList.filter ((<) 100) |> appliedToFunc list0to9 |> producesEquivalentOf []) |> testCase "returns empty list if predicate matches no items" (nameof(FlatList.filter)) + (FlatList.removeRange 0 0 |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list - 0 items from 0" (nameof(FlatList.removeRange)) + (FlatList.removeRange 0 -1 |> appliedToFunc emptyList |> throws) |> testCase "empty list - -1 items from 0" (nameof(FlatList.removeRange)) + (FlatList.removeRange -1 0 |> appliedToFunc emptyList |> throws) |> testCase "empty list - 0 items from -1" (nameof(FlatList.removeRange)) + (FlatList.removeRange 0 3 |> appliedToFunc emptyList |> throws) |> testCase "empty list - 3 items from 0" (nameof(FlatList.removeRange)) + (FlatList.removeRange 0 3 |> appliedToFunc list0to9 |> producesEquivalentOf [3..9]) |> testCase "non-empty list - 3 items from 0" (nameof(FlatList.removeRange)) + (FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 3 |> appliedToFunc emptyList |> throws) |> testCase "empty list - 3 items from 0" (nameof(FlatList.sortRangeWith)) + (FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 -1 |> appliedToFunc emptyList |> throws) |> testCase "empty list - -1 items from 0" (nameof(FlatList.sortRangeWith)) + (FlatList.sortRangeWith LanguagePrimitives.GenericComparison -1 0 |> appliedToFunc emptyList |> throws) |> testCase "empty list - 0 items from -1" (nameof(FlatList.sortRangeWith)) + (FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 0 |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list - 3 items from 0" (nameof(FlatList.sortRangeWith)) + (FlatList.sortRangeWith LanguagePrimitives.GenericComparison 0 3 |> appliedToFunc list9to0 |> producesSameAs [7;8;9;6;5;4;3;2;1;0]) |> testCase "9 to 0 list - 3 items from 0" (nameof(FlatList.sortRangeWith)) + (FlatList.sortRangeWith (fun x y -> y - x) 0 3 |> appliedToFunc list0to9 |> producesSameAs [2;1;0;3;4;5;6;7;8;9]) |> testCase "0 to 9 list - 3 items from 0" (nameof(FlatList.sortRangeWith)) + (FlatList.initWithValue -1 |> appliedTo 1 |> throws) |> testCase "-1 items" (nameof(FlatList.initWithValue)) + (FlatList.initWithValue 0 |> appliedTo 1 |> producesEquivalentOf []) |> testCase "0 items" (nameof(FlatList.initWithValue)) + (FlatList.initWithValue 3 |> appliedTo 1 |> producesEquivalentOf [1;1;1]) |> testCase "3 items" (nameof(FlatList.initWithValue)) + (FlatList.concat |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "no items" (nameof(FlatList.concat)) + (FlatList.concat |> appliedTo (listOf 4 <| emptyList ()) |> producesEquivalentOf []) |> testCase "no items 4 times" (nameof(FlatList.concat)) + (FlatList.concat |> appliedTo ([emptyList ();list0to9 ()] |> FlatList.ofList) |> producesEquivalentOf [0..9]) |> testCase "no items & 0 to 9" (nameof(FlatList.concat)) + (FlatList.concat |> appliedTo ([list0to9 ();list0to9 ()] |> FlatList.ofList) |> producesEquivalentOf [0;1;2;3;4;5;6;7;8;9;0;1;2;3;4;5;6;7;8;9]) |> testCase "0 to 9 times 2" (nameof(FlatList.concat)) + (FlatList.map ((+) 3) |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list" (nameof(FlatList.map)) + (FlatList.map ((+) 3) |> appliedToFunc list0to9 |> producesEquivalentOf [3..12]) |> testCase "non-empty list" (nameof(FlatList.map)) + (FlatList.countBy (fun x -> x % 2 = 0) |> appliedToFunc list0to9 |> producesEquivalentOf [true,5;false,5]) |> testCase "non-empty list" (nameof(FlatList.countBy)) + (FlatList.countBy (fun x -> x % 2 = 0) |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list" (nameof(FlatList.countBy)) + (FlatList.distinct |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list" (nameof(FlatList.distinct)) + (FlatList.distinct |> appliedTo ([1;1;1;1] |> FlatList.ofList) |> producesEquivalentOf [1]) |> testCase "duplicates list" (nameof(FlatList.distinct)) + (FlatList.distinctBy (fun x -> x % 2 = 0) |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list" (nameof(FlatList.distinctBy)) + (FlatList.distinctBy (fun x -> x % 2 = 0) |> appliedToFunc list0to9 |> producesEquivalentOf [0;1]) |> testCase "0 to 9 list, oddity check" (nameof(FlatList.distinctBy)) ] [] diff --git a/FSharp.Collections.Immutable.Tests/test-helpers.fs b/FSharp.Collections.Immutable.Tests/test-helpers.fs index 48b0973..f0a6754 100644 --- a/FSharp.Collections.Immutable.Tests/test-helpers.fs +++ b/FSharp.Collections.Immutable.Tests/test-helpers.fs @@ -6,12 +6,15 @@ open NUnit.Framework [] module TestHelpers = let appliedTo value func = fun () -> func value + let appliedToFunc value func = fun () -> func (value ()) let produces value func = fun () -> () |> func |> should equal value let producesEquivalentOf value func = fun () -> () |> func |> should equivalent value + let producesSameAs value func = fun () -> () |> func |> Seq.fold2 (fun acc x y -> acc && (x = y)) true value |> should equal true let throws func = fun () -> Assert.Catch (func >> ignore) |> ignore let doesNotThrow func = fun () -> func () |> ignore |> should equal () let noopPredicate _ = true let ignore2 _ _ = () let ignore3 _ _ _ = () let testCase name category (code:unit->unit) = ((TestCaseData code).SetName (category + " - " + name)).SetCategory category - + let withSecondArg a f = fun b -> f b a + let withSecondArgFrom a f = fun b -> f b (a ()) From 3f80dad4dfe0ad5b26b83417d73ce4650bdff0ad Mon Sep 17 00:00:00 2001 From: Andrey Sayapin Date: Mon, 12 Jul 2021 13:30:39 +0300 Subject: [PATCH 24/24] more tests --- .../flat-list-tests.fs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/FSharp.Collections.Immutable.Tests/flat-list-tests.fs b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs index 177e1ef..9951fb1 100644 --- a/FSharp.Collections.Immutable.Tests/flat-list-tests.fs +++ b/FSharp.Collections.Immutable.Tests/flat-list-tests.fs @@ -11,7 +11,7 @@ module FlatListTestsUtils = let list9to0 () = FlatList.init 10 ((-) 9) let listOf count list = let builder = FlatList.builderWith count - for i = 0 to count do + for i = 1 to count do builder.Add list FlatList.ofBuilder builder let throwsOnlyOnNullLists category func = [ @@ -137,6 +137,16 @@ type FlatListFixture () = (FlatList.distinct |> appliedTo ([1;1;1;1] |> FlatList.ofList) |> producesEquivalentOf [1]) |> testCase "duplicates list" (nameof(FlatList.distinct)) (FlatList.distinctBy (fun x -> x % 2 = 0) |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list" (nameof(FlatList.distinctBy)) (FlatList.distinctBy (fun x -> x % 2 = 0) |> appliedToFunc list0to9 |> producesEquivalentOf [0;1]) |> testCase "0 to 9 list, oddity check" (nameof(FlatList.distinctBy)) + (FlatList.map2 (+) |> withSecondArgFrom emptyList |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list - both" (nameof(FlatList.map2)) + (FlatList.map2 (+) |> withSecondArgFrom emptyList |> appliedToFunc list0to9 |> producesEquivalentOf []) |> testCase "empty list - 1st" (nameof(FlatList.map2)) + (FlatList.map2 (+) |> withSecondArgFrom list0to9 |> appliedToFunc emptyList |> producesEquivalentOf []) |> testCase "empty list - 2nd" (nameof(FlatList.map2)) + (FlatList.map2 (+) |> withSecondArgFrom list0to9 |> appliedToFunc list9to0 |> producesEquivalentOf (listOf 10 9)) |> testCase "non-empty" (nameof(FlatList.map2)) + (FlatList.exists ((>) 5) |> appliedToFunc emptyList |> produces false) |> testCase "empty list" (nameof(FlatList.exists)) + (FlatList.exists ((>) 5) |> appliedToFunc list0to9 |> produces true) |> testCase "non-empty list" (nameof(FlatList.exists)) + (FlatList.exists ((>) -1) |> appliedToFunc list0to9 |> produces false) |> testCase "non-empty list, invalid condition" (nameof(FlatList.exists)) + (FlatList.contains 5 |> appliedToFunc emptyList |> produces false) |> testCase "empty list" (nameof(FlatList.contains)) + (FlatList.contains 5 |> appliedToFunc list0to9 |> produces true) |> testCase "non-empty list" (nameof(FlatList.contains)) + (FlatList.contains -1 |> appliedToFunc list0to9 |> produces false) |> testCase "non-empty list, invalid element" (nameof(FlatList.contains)) ] []