diff --git a/src/FSharpx.Collections/PersistentVector.fs b/src/FSharpx.Collections/PersistentVector.fs index 656b5be8..484567ad 100644 --- a/src/FSharpx.Collections/PersistentVector.fs +++ b/src/FSharpx.Collections/PersistentVector.fs @@ -592,6 +592,102 @@ module PersistentVector = | Some v' -> tryNth j v' | None -> None + let choose (f: 'T -> 'T1 option) (vector: PersistentVector<'T>) : PersistentVector<'T1> = + let mutable ret = TransientVector() + + for item in vector do + match f item with + | Some v -> ret <- ret.conj v + | None -> () + + ret.persistent() + + let exists (f: 'T -> bool) (vector: PersistentVector<'T>) = + let mutable found = false + let mutable i = 0 + + while not found && i < vector.Length do + if f vector.[i] then + found <- true + + i <- i + 1 + + found + + let filter (f: 'T -> bool) (vector: PersistentVector<'T>) : PersistentVector<'T> = + let mutable ret = TransientVector() + + for item in vector do + if f item then + ret <- ret.conj item + + ret.persistent() + + let tryFindIndex (f: 'T -> bool) (vector: PersistentVector<'T>) = + let mutable result = -1 + let mutable i = 0 + + while result = -1 && i < vector.Length do + if f vector.[i] then + result <- i + + i <- i + 1 + + if result = -1 then None else Some result + + let findIndex (f: 'T -> bool) (vector: PersistentVector<'T>) = + match tryFindIndex f vector with + | Some i -> i + | None -> raise(System.Collections.Generic.KeyNotFoundException("An index satisfying the predicate was not found in the collection")) + + let find (f: 'T -> bool) (vector: PersistentVector<'T>) = + vector.[findIndex f vector] + + let tryFind (f: 'T -> bool) (vector: PersistentVector<'T>) = + match tryFindIndex f vector with + | Some i -> Some vector.[i] + | None -> None + + let forall (f: 'T -> bool) (vector: PersistentVector<'T>) = + let mutable allMatch = true + let mutable i = 0 + + while allMatch && i < vector.Length do + if not(f vector.[i]) then + allMatch <- false + + i <- i + 1 + + allMatch + + let inline head(vector: PersistentVector<'T>) = + if vector.IsEmpty then + invalidArg "vector" "The input vector was empty." + else + vector.[0] + + let inline tryHead(vector: PersistentVector<'T>) = + if vector.IsEmpty then None else Some vector.[0] + + let iter (f: 'T -> unit) (vector: PersistentVector<'T>) = + for item in vector do + f item + + let iteri (f: int -> 'T -> unit) (vector: PersistentVector<'T>) = + let mutable i = 0 + + for item in vector do + f i item + i <- i + 1 + + let ofList(items: 'T list) : PersistentVector<'T> = + let mutable ret = TransientVector() + + for item in items do + ret <- ret.conj item + + ret.persistent() + let ofSeq(items: 'T seq) = PersistentVector.ofSeq items @@ -601,6 +697,19 @@ module PersistentVector = let inline singleton(x: 'T) = empty |> conj x + let toArray(vector: PersistentVector<'T>) = + let arr = Array.zeroCreate vector.Length + let mutable i = 0 + + for item in vector do + arr.[i] <- item + i <- i + 1 + + arr + + let toList(vector: PersistentVector<'T>) : 'T list = + foldBack (fun item acc -> item :: acc) vector [] + let rangedIterator (startIndex: int) (endIndex: int) (vector: PersistentVector<'T>) = vector.rangedIterator(startIndex, endIndex) diff --git a/src/FSharpx.Collections/PersistentVector.fsi b/src/FSharpx.Collections/PersistentVector.fsi index 0304ec54..bab80122 100644 --- a/src/FSharpx.Collections/PersistentVector.fsi +++ b/src/FSharpx.Collections/PersistentVector.fsi @@ -118,6 +118,36 @@ module PersistentVector = /// O(log32(m,n)). Returns option value at the indices. val inline tryNthNth: int -> int -> PersistentVector> -> 'T option + /// O(n). Returns a new vector containing only elements for which the supplied function returns Some. + val choose: ('T -> 'T1 option) -> PersistentVector<'T> -> PersistentVector<'T1> + + /// O(n). Returns true if the given predicate returns true for some element in the vector. + val exists: ('T -> bool) -> PersistentVector<'T> -> bool + + /// O(n). Returns a new vector containing only the elements of the supplied vector for which the given predicate returns true. + val filter: ('T -> bool) -> PersistentVector<'T> -> PersistentVector<'T> + + /// O(n). Returns the index of the first element in the vector that satisfies the given predicate. Raises KeyNotFoundException if not found. + val findIndex: ('T -> bool) -> PersistentVector<'T> -> int + + /// O(n). Returns the first element in the vector that satisfies the given predicate. Raises KeyNotFoundException if not found. + val find: ('T -> bool) -> PersistentVector<'T> -> 'T + + /// O(n). Returns true if the given predicate returns true for all elements in the vector. + val forall: ('T -> bool) -> PersistentVector<'T> -> bool + + /// O(1) for all practical purposes; really O(log32n). Returns the first element in the vector. Raises ArgumentException if the vector is empty. + val inline head: PersistentVector<'T> -> 'T + + /// O(n). Applies the given function to each element of the vector. + val iter: ('T -> unit) -> PersistentVector<'T> -> unit + + /// O(n). Applies the given function to each element of the vector, passing the index as the first argument. + val iteri: (int -> 'T -> unit) -> PersistentVector<'T> -> unit + + /// O(n). Returns a new vector from the supplied list. + val ofList: 'T list -> PersistentVector<'T> + /// O(n). Returns a vector of the seq. val ofSeq: seq<'T> -> PersistentVector<'T> @@ -131,9 +161,24 @@ module PersistentVector = /// `rangedIterator 0 count` is the same as toSeq val rangedIterator: int -> int -> PersistentVector<'T> -> seq<'T> + /// O(n). Returns the elements of the vector as an array. + val toArray: PersistentVector<'T> -> 'T[] + + /// O(n). Returns the elements of the vector as a list. + val toList: PersistentVector<'T> -> 'T list + /// O(n). Views the given vector as a sequence. val inline toSeq: PersistentVector<'T> -> seq<'T> + /// O(n). Returns the index of the first element in the vector that satisfies the given predicate, or None if not found. + val tryFindIndex: ('T -> bool) -> PersistentVector<'T> -> int option + + /// O(n). Returns the first element in the vector that satisfies the given predicate, or None if not found. + val tryFind: ('T -> bool) -> PersistentVector<'T> -> 'T option + + /// O(1) for all practical purposes; really O(log32n). Returns option first element in the vector. + val inline tryHead: PersistentVector<'T> -> 'T option + /// O(1) for all practical purposes; really O(log32n). Returns tuple last element and vector without last item val inline unconj: PersistentVector<'T> -> PersistentVector<'T> * 'T diff --git a/tests/FSharpx.Collections.Tests/PersistentVectorTest.fs b/tests/FSharpx.Collections.Tests/PersistentVectorTest.fs index 851fda98..c3a85e31 100644 --- a/tests/FSharpx.Collections.Tests/PersistentVectorTest.fs +++ b/tests/FSharpx.Collections.Tests/PersistentVectorTest.fs @@ -445,6 +445,165 @@ module PersistentVectorTests = (i - 1) + i * 2 |> Expect.equal "map" a.[i - 1] } + test "filter should return elements satisfying predicate" { + let v = PersistentVector.ofSeq [ 1; 2; 3; 4; 5; 6 ] + let result = PersistentVector.filter (fun x -> x % 2 = 0) v + Expect.equal "filter even" [ 2; 4; 6 ] (result |> Seq.toList) + } + + test "filter empty vector returns empty" { + let result = PersistentVector.filter (fun x -> x > 0) PersistentVector.empty + Expect.equal "filter empty" 0 (result |> PersistentVector.length) + } + + test "filter all excluded returns empty" { + let v = PersistentVector.ofSeq [ 1; 3; 5 ] + let result = PersistentVector.filter (fun x -> x % 2 = 0) v + Expect.equal "filter none" 0 (result |> PersistentVector.length) + } + + test "iter applies function to each element" { + let v = PersistentVector.ofSeq [ 1; 2; 3 ] + let mutable sum = 0 + PersistentVector.iter (fun x -> sum <- sum + x) v + Expect.equal "iter sum" 6 sum + } + + test "iter on empty vector does nothing" { + let mutable count = 0 + PersistentVector.iter (fun _ -> count <- count + 1) PersistentVector.empty + Expect.equal "iter empty" 0 count + } + + test "iteri passes correct indices" { + let v = PersistentVector.ofSeq [ 10; 20; 30 ] + let mutable idxSum = 0 + PersistentVector.iteri (fun i _ -> idxSum <- idxSum + i) v + Expect.equal "iteri indices" 3 idxSum // 0+1+2 + } + + test "exists returns true when element found" { + let v = PersistentVector.ofSeq [ 1; 2; 3 ] + Expect.isTrue "exists" (PersistentVector.exists (fun x -> x = 2) v) + } + + test "exists returns false when no element found" { + let v = PersistentVector.ofSeq [ 1; 2; 3 ] + Expect.isFalse "exists none" (PersistentVector.exists (fun x -> x = 9) v) + } + + test "exists on empty returns false" { + Expect.isFalse "exists empty" (PersistentVector.exists (fun _ -> true) PersistentVector.empty) + } + + test "forall returns true when all elements match" { + let v = PersistentVector.ofSeq [ 2; 4; 6 ] + Expect.isTrue "forall even" (PersistentVector.forall (fun x -> x % 2 = 0) v) + } + + test "forall returns false when some element does not match" { + let v = PersistentVector.ofSeq [ 2; 3; 6 ] + Expect.isFalse "forall not all even" (PersistentVector.forall (fun x -> x % 2 = 0) v) + } + + test "forall on empty returns true" { + Expect.isTrue "forall empty" (PersistentVector.forall (fun _ -> false) PersistentVector.empty) + } + + test "find returns first matching element" { + let v = PersistentVector.ofSeq [ 1; 2; 3; 4 ] + Expect.equal "find" 3 (PersistentVector.find (fun x -> x > 2) v) + } + + test "find throws when not found" { + let v = PersistentVector.ofSeq [ 1; 2; 3 ] + Expect.throws "find throws" (fun () -> PersistentVector.find (fun x -> x > 9) v |> ignore) + } + + test "tryFind returns Some for matching element" { + let v = PersistentVector.ofSeq [ 1; 2; 3 ] + Expect.equal "tryFind" (Some 2) (PersistentVector.tryFind (fun x -> x = 2) v) + } + + test "tryFind returns None when not found" { + let v = PersistentVector.ofSeq [ 1; 2; 3 ] + Expect.equal "tryFind none" None (PersistentVector.tryFind (fun x -> x = 9) v) + } + + test "findIndex returns index of first matching element" { + let v = PersistentVector.ofSeq [ 10; 20; 30; 20 ] + Expect.equal "findIndex" 1 (PersistentVector.findIndex (fun x -> x = 20) v) + } + + test "findIndex throws KeyNotFoundException when not found" { + let v = PersistentVector.ofSeq [ 1; 2; 3 ] + + Expect.throws "findIndex not found" (fun () -> PersistentVector.findIndex (fun x -> x = 99) v |> ignore) + } + + test "findIndex throws KeyNotFoundException on empty vector" { + Expect.throws "findIndex empty" (fun () -> + PersistentVector.findIndex (fun _ -> true) PersistentVector.empty + |> ignore) + } + + test "tryFindIndex returns Some index when found" { + let v = PersistentVector.ofSeq [ 10; 20; 30 ] + Expect.equal "tryFindIndex" (Some 2) (PersistentVector.tryFindIndex (fun x -> x = 30) v) + } + + test "tryFindIndex returns None when not found" { + let v = PersistentVector.ofSeq [ 1; 2; 3 ] + Expect.equal "tryFindIndex none" None (PersistentVector.tryFindIndex (fun x -> x = 99) v) + } + + test "choose returns mapped Some values" { + let v = PersistentVector.ofSeq [ 1; 2; 3; 4; 5 ] + + let result = + PersistentVector.choose (fun x -> if x % 2 = 0 then Some(x * 10) else None) v + + Expect.equal "choose" [ 20; 40 ] (result |> Seq.toList) + } + + test "head returns first element" { + let v = PersistentVector.ofSeq [ 7; 8; 9 ] + Expect.equal "head" 7 (PersistentVector.head v) + } + + test "head throws on empty" { Expect.throws "head empty" (fun () -> PersistentVector.head PersistentVector.empty |> ignore) } + + test "tryHead returns Some for non-empty" { + let v = PersistentVector.ofSeq [ 42; 43 ] + Expect.equal "tryHead" (Some 42) (PersistentVector.tryHead v) + } + + test "tryHead returns None for empty" { Expect.equal "tryHead empty" None (PersistentVector.tryHead PersistentVector.empty) } + + test "toArray returns all elements" { + let v = PersistentVector.ofSeq [ 1; 2; 3 ] + Expect.equal "toArray" [| 1; 2; 3 |] (PersistentVector.toArray v) + } + + test "toArray on empty returns empty array" { Expect.equal "toArray empty" [||] (PersistentVector.toArray PersistentVector.empty) } + + test "toList returns all elements as list" { + let v = PersistentVector.ofSeq [ 1; 2; 3 ] + Expect.equal "toList" [ 1; 2; 3 ] (PersistentVector.toList v) + } + + test "ofList creates vector from list" { + let v = PersistentVector.ofList [ 5; 6; 7 ] + Expect.equal "ofList length" 3 (PersistentVector.length v) + Expect.equal "ofList nth 0" 5 (PersistentVector.nth 0 v) + Expect.equal "ofList nth 2" 7 (PersistentVector.nth 2 v) + } + + test "ofList empty creates empty vector" { + let v = PersistentVector.ofList [] + Expect.equal "ofList empty" 0 (PersistentVector.length v) + } + test "vector should allow init" { let vector = PersistentVector.init 5 (fun x -> x * 2) let s = Seq.init 5 (fun x -> x * 2)