diff --git a/src/FSharpx.Collections/DList.fs b/src/FSharpx.Collections/DList.fs index 7db24add..6b455cb2 100644 --- a/src/FSharpx.Collections/DList.fs +++ b/src/FSharpx.Collections/DList.fs @@ -279,3 +279,54 @@ module DList = let inline toArray(l: DList<'T>) : 'T[] = Seq.toArray l + + ///O(n). Build a DList from the given list. + let inline ofList(l: 'T list) : DList<'T> = ofSeq l + + ///O(n). Build a DList from the given array. + let inline ofArray(a: 'T array) : DList<'T> = ofSeq a + + ///O(n), worst case. Returns the first element for which the given function returns Some. + let tryFind (predicate: 'T -> bool) (l: DList<'T>) : 'T option = + Seq.tryFind predicate l + + ///O(n), worst case. Returns the first element for which the given function returns true. + /// Raises KeyNotFoundException if no such element exists. + let find (predicate: 'T -> bool) (l: DList<'T>) : 'T = + Seq.find predicate l + + ///O(n). Applies the given function to each element and returns a DList of the values returned + /// by the function where the function returned Some. + let choose (mapping: 'T -> 'U option) (l: DList<'T>) : DList<'U> = + foldBack + (fun x acc -> + match mapping x with + | Some v -> cons v acc + | None -> acc) + l + empty + + ///O(n). For each element, applies the given function to produce a DList, then concatenates all results. + let collect (mapping: 'T -> DList<'U>) (l: DList<'T>) : DList<'U> = + foldBack (fun x acc -> append (mapping x) acc) l empty + + ///O(n). Splits the DList into two DLists: the first contains elements for which the predicate + /// returns true; the second contains those for which it returns false. + let partition (predicate: 'T -> bool) (l: DList<'T>) : DList<'T> * DList<'T> = + foldBack (fun x (yes, no) -> if predicate x then cons x yes, no else yes, cons x no) l (empty, empty) + + ///O(n log n). Returns a new DList sorted using the default comparison. + let sort(l: DList<'T>) : DList<'T> = + l |> toArray |> Array.sort |> ofArray + + ///O(n log n). Returns a new DList sorted using the given comparison function. + let sortWith (comparer: 'T -> 'T -> int) (l: DList<'T>) : DList<'T> = + l |> toArray |> Array.sortWith comparer |> ofArray + + ///O(n log n). Returns a new DList sorted by the given projection. + let sortBy (projection: 'T -> 'Key) (l: DList<'T>) : DList<'T> = + l |> toArray |> Array.sortBy projection |> ofArray + + ///O(n). Returns the DList in reversed order. + let rev(l: DList<'T>) : DList<'T> = + fold (fun acc x -> cons x acc) empty l diff --git a/src/FSharpx.Collections/DList.fsi b/src/FSharpx.Collections/DList.fsi index 894f06c1..15cebe9c 100644 --- a/src/FSharpx.Collections/DList.fsi +++ b/src/FSharpx.Collections/DList.fsi @@ -121,3 +121,37 @@ module DList = ///O(n). Returns an array of the DList elements. val inline toArray: DList<'T> -> 'T[] + + ///O(n). Build a DList from the given list. + val inline ofList: list<'T> -> DList<'T> + + ///O(n). Build a DList from the given array. + val inline ofArray: 'T[] -> DList<'T> + + ///O(n), worst case. Returns the first element for which the given function returns Some. + val tryFind: ('T -> bool) -> DList<'T> -> 'T option + + ///O(n), worst case. Returns the first element for which the given function returns true. + /// Raises KeyNotFoundException if no such element exists. + val find: ('T -> bool) -> DList<'T> -> 'T + + ///O(n). Returns a DList of the values v where the mapping function returns Some(v). + val choose: ('T -> 'U option) -> DList<'T> -> DList<'U> + + ///O(n). For each element, applies the mapping to produce a DList, then concatenates all results. + val collect: ('T -> DList<'U>) -> DList<'T> -> DList<'U> + + ///O(n). Splits the DList into two DLists on the predicate: true elements first, false elements second. + val partition: ('T -> bool) -> DList<'T> -> DList<'T> * DList<'T> + + ///O(n log n). Returns a new DList sorted using the default comparison. + val sort: DList<'T> -> DList<'T> when 'T: comparison + + ///O(n log n). Returns a new DList sorted using the given comparison function. + val sortWith: ('T -> 'T -> int) -> DList<'T> -> DList<'T> + + ///O(n log n). Returns a new DList sorted by the given projection. + val sortBy: ('T -> 'Key) -> DList<'T> -> DList<'T> when 'Key: comparison + + ///O(n). Returns the DList in reversed order. + val rev: DList<'T> -> DList<'T> diff --git a/tests/FSharpx.Collections.Tests/DListTest.fs b/tests/FSharpx.Collections.Tests/DListTest.fs index cfa6fdc0..1edda4ed 100644 --- a/tests/FSharpx.Collections.Tests/DListTest.fs +++ b/tests/FSharpx.Collections.Tests/DListTest.fs @@ -494,4 +494,90 @@ module DListTests = config10k "DList.toArray matches Seq.toArray 0" (Prop.forAll(Arb.fromGen intGensStart1.[0]) - <| fun (q, l) -> DList.toArray q = Array.ofList l) ] + <| fun (q, l) -> DList.toArray q = Array.ofList l) + + testPropertyWithConfig + config10k + "DList.ofList round-trips with toList" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (_, l) -> DList.ofList l |> DList.toList = l) + + testPropertyWithConfig + config10k + "DList.ofArray round-trips with toArray" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (_, l) -> DList.ofArray(Array.ofList l) |> DList.toArray = Array.ofList l) + + testPropertyWithConfig + config10k + "DList.tryFind matches List.tryFind" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (q, l) -> DList.tryFind (fun x -> x % 3 = 0) q = List.tryFind (fun x -> x % 3 = 0) l) + + testPropertyWithConfig + config10k + "DList.find matches List.find when element exists" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (q, l) -> + match List.tryFind (fun x -> x % 2 = 0) l with + | Some expected -> DList.find (fun x -> x % 2 = 0) q = expected + | None -> true) + + testPropertyWithConfig + config10k + "DList.choose matches List.choose" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (q, l) -> + let mapping x = + if x % 2 = 0 then Some(x * 2) else None + + DList.choose mapping q |> DList.toList = List.choose mapping l) + + testPropertyWithConfig + config10k + "DList.collect matches List.collect" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (q, l) -> + let mapping x = + DList.ofList [ x; x * 2 ] + + DList.collect mapping q |> DList.toList = List.collect (fun x -> [ x; x * 2 ]) l) + + testPropertyWithConfig + config10k + "DList.partition splits correctly" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (q, l) -> + let trueD, falseD = DList.partition (fun x -> x % 2 = 0) q + let trueL, falseL = List.partition (fun x -> x % 2 = 0) l + DList.toList trueD = trueL && DList.toList falseD = falseL) + + testPropertyWithConfig + config10k + "DList.sort matches List.sort" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (q, l) -> DList.sort q |> DList.toList = List.sort l) + + testPropertyWithConfig + config10k + "DList.sortWith compare matches List.sort" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (q, l) -> DList.sortWith compare q |> DList.toList = List.sort l) + + testPropertyWithConfig + config10k + "DList.sortBy id matches List.sort" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (q, l) -> DList.sortBy id q |> DList.toList = List.sort l) + + testPropertyWithConfig + config10k + "DList.rev reverses the list" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (q, l) -> DList.rev q |> DList.toList = List.rev l) + + testPropertyWithConfig + config10k + "DList.rev . DList.rev = id" + (Prop.forAll(Arb.fromGen intGensStart1.[0]) + <| fun (q, _) -> DList.rev(DList.rev q) |> DList.toList = DList.toList q) ]