Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/FSharpx.Collections/DList.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <c>Some</c>.
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 <c>true</c>.
/// Raises <c>KeyNotFoundException</c> 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 <c>Some</c>.
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 <c>true</c>; the second contains those for which it returns <c>false</c>.
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
34 changes: 34 additions & 0 deletions src/FSharpx.Collections/DList.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -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>
88 changes: 87 additions & 1 deletion tests/FSharpx.Collections.Tests/DListTest.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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) ]
Loading