Skip to content
Open
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
109 changes: 109 additions & 0 deletions src/FSharpx.Collections/PersistentVector.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)

Expand Down
45 changes: 45 additions & 0 deletions src/FSharpx.Collections/PersistentVector.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,36 @@ module PersistentVector =
/// O(log32(m,n)). Returns option value at the indices.
val inline tryNthNth: int -> int -> PersistentVector<PersistentVector<'T>> -> '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>

Expand All @@ -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

Expand Down
159 changes: 159 additions & 0 deletions tests/FSharpx.Collections.Tests/PersistentVectorTest.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>
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<int>
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<int>)
}

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<int>)
}

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)
}
Comment on lines +533 to +536
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@copilot add it


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<int>
|> 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<int> |> 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<int>) }

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<int>) }

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)
Expand Down