Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve RandomAccessList API with some functions from PersistentVector #54

Merged
merged 3 commits into from
May 25, 2016
Merged
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
4 changes: 2 additions & 2 deletions src/FSharpx.Collections/PersistentVector.fs
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ module PersistentVector =

let inline tryUpdate i (x : 'T) (vector : PersistentVector<'T>) = vector.TryUpdate(i, x)

let inline tryUpdateNth i j (x : 'T) (vector : PersistentVector<PersistentVector<'T>>) =
let inline tryUpdateNth i j (x : 'T) (vector : PersistentVector<PersistentVector<'T>>) =
if i >= 0 && i < vector.Length && j >= 0 && j < vector.[i].Length
then Some(updateNth i j x vector)
else None
Expand All @@ -464,4 +464,4 @@ module PersistentVector =
let inline windowSeq windowLength (items : 'T seq) =
if windowLength < 1 then invalidArg "windowLength" "length is less than 1"
else (Seq.fold (windowFun windowLength) (empty.Conj empty<'T>) items)
#endif
#endif
2 changes: 1 addition & 1 deletion src/FSharpx.Collections/PersistentVector.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ module PersistentVector =
/// O(1) for all practical purposes; really O(log32n). Returns the value at the index. If the index is out of bounds it throws an exception.
val inline nth : int -> PersistentVector<'T> -> 'T

/// O(log32(m,n)). Returns the value at the outer index, inner index. If either index is out of bounds it throws an exception.
/// O(log32(m,n)). Returns the value at the outer index, inner index. If either index is out of bounds it throws an exception.
val inline nthNth : int -> int -> PersistentVector<PersistentVector<'T>> -> 'T

/// O(1) for all practical purposes; really O(log32n). Returns option value at the index.
Expand Down
43 changes: 40 additions & 3 deletions src/FSharpx.Collections/RandomAccessList.fs
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,15 @@ and RandomAccessList<'T> (count,shift:int,root:NodeR,tail:obj[]) =
module RandomAccessList =
//pattern discriminators (active pattern)
let (|Cons|Nil|) (v : RandomAccessList<'T>) = match v.TryUncons with Some(a,b) -> Cons(a,b) | None -> Nil


let append (listA : RandomAccessList<'T>) (listB : RandomAccessList<'T>) =
let mutable ret = TransientVect()
for i in (listB.Length - 1) .. -1 .. 0 do
ret <- ret.conj listB.[i]
for i in (listA.Length - 1) .. -1 .. 0 do
ret <- ret.conj listA.[i]
ret.persistent()

let inline cons (x : 'T) (randomAccessList : 'T RandomAccessList) = randomAccessList.Cons x

let empty<'T> = RandomAccessList.Empty() :> RandomAccessList<'T>
Expand Down Expand Up @@ -381,11 +389,20 @@ module RandomAccessList =
let inline tryNth i (randomAccessList :'T RandomAccessList) =
if i >= 0 && i < randomAccessList.Length then Some(randomAccessList.[i])
else None


let inline nthNth i j (randomAccessList :'T RandomAccessList RandomAccessList) : 'T = randomAccessList.[i] |> nth j

let inline tryNthNth i j (randomAccessList :'T RandomAccessList RandomAccessList) =
match tryNth i randomAccessList with
| Some v' -> tryNth j v'
| None -> None

let ofSeq (items : 'T seq) = RandomAccessList.ofSeq items

let inline rev (randomAccessList :'T RandomAccessList) = randomAccessList.Rev()

let inline singleton (x : 'T) = empty |> cons x

let inline tail (randomAccessList :'T RandomAccessList) = randomAccessList.Tail

let inline tryTail (randomAccessList :'T RandomAccessList) = randomAccessList.TryTail
Expand All @@ -398,6 +415,26 @@ module RandomAccessList =

let inline update i (x : 'T) (randomAccessList : 'T RandomAccessList) = randomAccessList.Update(i, x)

let inline updateNth i j (x : 'T) (randomAccessList : 'T RandomAccessList RandomAccessList) : 'T RandomAccessList RandomAccessList = randomAccessList.Update(i, (randomAccessList.[i].Update(j, x)))

let inline tryUpdate i (x : 'T) (randomAccessList : 'T RandomAccessList) = randomAccessList.TryUpdate(i, x)

#endif
let inline tryUpdateNth i j (x : 'T) (randomAccessList : 'T RandomAccessList RandomAccessList) =
if i >= 0 && i < randomAccessList.Length && j >= 0 && j < randomAccessList.[i].Length
then Some(updateNth i j x randomAccessList)
else None

let inline windowFun windowLength =
fun t (v : RandomAccessList<RandomAccessList<'T>>) ->
if v.Head.Length = windowLength
then
v
|> cons (empty.Cons(t))
else
tail v
|> cons (head v |> cons t)

let inline windowSeq windowLength (items: 'T seq) =
if windowLength < 1 then invalidArg "windowLength" "length is less than 1"
else (Seq.foldBack (windowFun windowLength) items (empty.Cons empty<'T>)) (*Seq.fold (windowFun windowLength) (empty.Cons empty<'T>) items*) // TODO: Check if this should be foldBack due to inversion effects of prepending
#endif
21 changes: 21 additions & 0 deletions src/FSharpx.Collections/RandomAccessList.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ module RandomAccessList =
//pattern discriminators (active pattern)
val (|Cons|Nil|) : RandomAccessList<'T> -> Choice<('T * RandomAccessList<'T> ),unit>

/// O(n). Returns a new random access list with the elements of the second random access list added at the end.
val append : RandomAccessList<'T> -> RandomAccessList<'T> -> RandomAccessList<'T>

/// O(1). Returns a new random access list with the element added at the start.
val inline cons : 'T -> RandomAccessList<'T> -> RandomAccessList<'T>

Expand Down Expand Up @@ -97,12 +100,21 @@ module RandomAccessList =
/// O(1) for all practical purposes; really O(log32n). Returns option value at the index.
val inline tryNth : int -> RandomAccessList<'T> -> 'T option

/// O(log32(m,n)). Returns the value at the outer index, inner index. If either index is out of bounds it throws an exception.
val inline nthNth : int -> int -> RandomAccessList<RandomAccessList<'T>> -> 'T

/// O(log32(m,n)). Returns option value at the indices.
val inline tryNthNth : int -> int -> RandomAccessList<RandomAccessList<'T>> -> 'T option

/// O(n). Returns a random access list of the seq.
val ofSeq : seq<'T> -> RandomAccessList<'T>

/// O(n). Returns new random access list reversed.
val inline rev : RandomAccessList<'T> -> RandomAccessList<'T>

/// O(1). Returns a new random access list of one element.
val inline singleton : 'T -> RandomAccessList<'T>

/// O(1) for all practical purposes; really O(log32n). Returns a new random access list without the first item. If the collection is empty it throws an exception.
val inline tail : RandomAccessList<'T> -> RandomAccessList<'T>

Expand All @@ -121,6 +133,15 @@ module RandomAccessList =
/// O(1) for all practical purposes; really O(log32n). Returns a new random access list that contains the given value at the index.
val inline update : int -> 'T -> RandomAccessList<'T> -> RandomAccessList<'T>

/// O(log32(m,n)). Returns a new random access list of random access lists that contains the given value at the indices.
val inline updateNth : int -> int -> 'T -> RandomAccessList<RandomAccessList<'T>> -> RandomAccessList<RandomAccessList<'T>>

/// O(1) for all practical purposes; really O(log32n). Returns option random access list that contains the given value at the index.
val inline tryUpdate : int -> 'T -> RandomAccessList<'T> -> RandomAccessList<'T> option

/// O(log32(m,n)). Returns option random access list that contains the given value at the indices.
val inline tryUpdateNth : int -> int -> 'T -> RandomAccessList<RandomAccessList<'T>> -> RandomAccessList<RandomAccessList<'T>> option

/// O(n). Returns a random access list of random access lists of given length from the seq. Result may be a jagged random access list.
val inline windowSeq : int -> seq<'T> -> RandomAccessList<RandomAccessList<'T>>
#endif
156 changes: 155 additions & 1 deletion tests/FSharpx.Collections.Tests/PersistentVectorTest.fs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,107 @@ let ``vector with 300 elements should allow assocN``() =
let a = !vector |> Seq.toArray
for i in 1..300 do i * 2 |> should equal a.[i-1]

[<Test>]
let ``vector of vectors can be accessed with nthNth``() =
let inner = [1; 2; 3; 4; 5] |> ofSeq
let outer = empty |> conj inner |> conj inner

outer |> nthNth 0 2 |> should equal 3
outer |> nthNth 1 4 |> should equal 5

[<Test>]
let ``nthNth throws exception for out-of-bounds indices``() =
let inner = [1; 2; 3; 4; 5] |> ofSeq
let outer = empty |> conj inner |> conj inner

(fun () -> nthNth 2 2 outer |> ignore) |> should throw typeof<System.IndexOutOfRangeException>
(fun () -> nthNth 1 5 outer |> ignore) |> should throw typeof<System.IndexOutOfRangeException>
(fun () -> nthNth -1 2 outer |> ignore) |> should throw typeof<System.IndexOutOfRangeException>
(fun () -> nthNth 1 -2 outer |> ignore) |> should throw typeof<System.IndexOutOfRangeException>

[<Test>]
let ``vector of vectors can be accessed with tryNthNth``() =
let inner = [1; 2; 3; 4; 5] |> ofSeq
let outer = empty |> conj inner |> conj inner

outer |> tryNthNth 0 2 |> should equal (Some 3)
outer |> tryNthNth 1 4 |> should equal (Some 5)

[<Test>]
let ``tryNthNth returns None for out-of-bounds indices``() =
let inner = [1; 2; 3; 4; 5] |> ofSeq
let outer = empty |> conj inner |> conj inner

outer |> tryNthNth 2 2 |> should equal None
outer |> tryNthNth 1 5 |> should equal None
outer |> tryNthNth -1 2 |> should equal None
outer |> tryNthNth 1 -2 |> should equal None

[<Test>]
let ``vector of vectors can be updated with updateNth``() =
let inner = [1; 2; 3; 4; 5] |> ofSeq
let outer = empty |> conj inner |> conj inner

outer |> updateNth 0 2 7 |> nthNth 0 2 |> should equal 7
outer |> updateNth 1 4 9 |> nthNth 1 4 |> should equal 9

[<Test>]
let ``updateNth should not change the original vector``() =
let inner = [1; 2; 3; 4; 5] |> ofSeq
let outer = ref empty
outer := conj inner (!outer)
outer := conj inner (!outer)

!outer |> updateNth 0 2 7 |> nthNth 0 2 |> should equal 7
!outer |> nthNth 0 2 |> should equal 3
!outer |> updateNth 1 4 9 |> nthNth 1 4 |> should equal 9
!outer |> nthNth 1 4 |> should equal 5

[<Test>]
let ``updateNth throws exception for out-of-bounds indices``() =
let inner = [1; 2; 3; 4; 5] |> ofSeq
let outer = empty |> conj inner |> conj inner

(fun () -> updateNth 0 6 7 outer |> ignore) |> should throw typeof<System.IndexOutOfRangeException>
(fun () -> updateNth 9 2 7 outer |> ignore) |> should throw typeof<System.IndexOutOfRangeException>
(fun () -> updateNth 1 -4 7 outer |> ignore) |> should throw typeof<System.IndexOutOfRangeException>
(fun () -> updateNth -1 4 7 outer |> ignore) |> should throw typeof<System.IndexOutOfRangeException>

[<Test>]
let ``tryUpdateNth returns None for out-of-bounds indices``() =
let inner = [1; 2; 3; 4; 5] |> ofSeq
let outer = empty |> conj inner |> conj inner

tryUpdateNth 0 6 7 outer |> should equal None
tryUpdateNth 9 2 7 outer |> should equal None
tryUpdateNth 1 -4 7 outer |> should equal None
tryUpdateNth -1 4 7 outer |> should equal None

[<Test>]
let ``tryUpdateNth is like updateNth but returns option``() =
let inner = [1; 2; 3; 4; 5] |> ofSeq
let outer = empty |> conj inner |> conj inner

let result = outer |> tryUpdateNth 0 2 7
result |> Option.isSome |> should be True
result |> Option.get |> nthNth 0 2 |> should equal 7

let result2 = outer |> tryUpdateNth 1 4 9
result2 |> Option.isSome |> should be True
result2 |> Option.get |> nthNth 1 4 |> should equal 9

[<Test>]
let ``tryUpdateNth should not change the original vector``() =
let inner = [1; 2; 3; 4; 5] |> ofSeq
let outer = ref empty
outer := conj inner (!outer)
outer := conj inner (!outer)

!outer |> tryUpdateNth 0 2 7 |> Option.get |> nthNth 0 2 |> should equal 7
!outer |> nthNth 0 2 |> should equal 3
!outer |> tryUpdateNth 1 4 9 |> Option.get |> nthNth 1 4 |> should equal 9
!outer |> nthNth 1 4 |> should equal 5

[<Test>]
let ``can peek elements from a vector``() =
let vector = empty |> conj 1 |> conj 4 |> conj 25
Expand Down Expand Up @@ -139,6 +240,18 @@ let ``vector with 3 elements can be compared``() =
vector1 = vector2 |> should equal true
vector1 = vector3 |> should equal false

[<Test>]
let ``appending two vectors keeps order of items``() =
let vector1 = ref empty
for i in 1..3 do
vector1 := conj i (!vector1)

let vector2 = ref empty
for i in 7..9 do
vector2 := conj i (!vector2)

append (!vector1) (!vector2) |> toSeq |> Seq.toList |> should equal [1;2;3;7;8;9]

[<Test>]
let ``vector should allow map``() =
let vector = ref empty
Expand All @@ -156,4 +269,45 @@ let ``vector should allow init``() =
let s = Seq.init 5 (fun x -> x * 2)

s |> Seq.toList |> should equal [0;2;4;6;8]
vector |> Seq.toList |> should equal [0;2;4;6;8]
vector |> Seq.toList |> should equal [0;2;4;6;8]

[<Test>]
let ``windowSeq should keep every value from its original list``() =
let seq30 = seq { for i in 1..30 do yield i }
let fullVec = ofSeq seq30
for i in 1..35 do
let vecs = windowSeq i seq30
vecs |> fold append empty |> should equal fullVec

[<Test>]
let ``windowSeq should return vectors of equal length if possible``() =
let seq30 = seq { for i in 1..30 do yield i }

let len3vecs = windowSeq 3 seq30
let len5vecs = windowSeq 5 seq30
let len6vecs = windowSeq 6 seq30

len3vecs |> length |> should equal 10
len5vecs |> length |> should equal 6
len6vecs |> length |> should equal 5
len3vecs |> map length |> toSeq |> Seq.toList |> should equal [3;3;3;3;3;3;3;3;3;3]
len5vecs |> map length |> toSeq |> Seq.toList |> should equal [5;5;5;5;5;5]
len6vecs |> map length |> toSeq |> Seq.toList |> should equal [6;6;6;6;6]

[<Test>]
let ``windowSeq should return vectors all of equal length except the last``() =
let seq30 = seq { for i in 1..30 do yield i }

let len4vecs = windowSeq 4 seq30
let len7vecs = windowSeq 7 seq30
let len8vecs = windowSeq 8 seq30
let len17vecs = windowSeq 17 seq30

len4vecs |> length |> should equal 8
len7vecs |> length |> should equal 5
len8vecs |> length |> should equal 4
len17vecs |> length |> should equal 2
len4vecs |> map length |> toSeq |> Seq.toList |> should equal [4;4;4;4;4;4;4;2]
len7vecs |> map length |> toSeq |> Seq.toList |> should equal [7;7;7;7;2]
len8vecs |> map length |> toSeq |> Seq.toList |> should equal [8;8;8;6]
len17vecs |> map length |> toSeq |> Seq.toList |> should equal [17;13]
Loading