FSharpx.Collections


PersistentVector

A Vector is a collection of values indexed by contiguous integers. Vectors support access to items by index in log32N hops. count is O(1). conj puts the item at the end of the vector. More details can be found in the API docs.

open FSharpx.Collections

// Create a new PersistentVector and add some items to it
let v = 
    PersistentVector.empty 
    |> PersistentVector.conj "hello"
    |> PersistentVector.conj "world"
    |> PersistentVector.conj "!"
seq ["hello"; "world"; "!"]
// lookup some items
PersistentVector.nth 0 v
"hello"
PersistentVector.nth 1 v
"world"
v.[2]
"!"
// Check no. of elements in the PersistentVector
PersistentVector.length v
3

PersistentVectors are immutable and therefor allow to create new version without destruction of the old ones.

let v' = 
    v
    |> PersistentVector.conj "!!!" 
    |> PersistentVector.update 0 "hi" // replace existing value

PersistentVector.nth 0 v'
"hi"
PersistentVector.nth 3 v'
"!!!"
PersistentVector.nth 0 v
"hello"
PersistentVector.length v
3
PersistentVector.length v'
4
// remove the last element from a PersistentVector
let v'' = PersistentVector.initial v'

PersistentVector.length v''
3

There a couple of interesting operations on PersistentVectors:

// Convert a sequence of values to a PersistentVectors
let intVector = 
    PersistentVector.ofSeq [1..10]
seq [1; 2; 3; 4; ...]
// Square all values in a PersistentVector
let intVector' = 
    PersistentVector.map (fun x -> x * x) intVector
intVector'.[3]
16

Using from C#

A Vector is a collection of values indexed by contiguous integers. Vectors support access to items by index in log32N hops. Count is O(1). Conj puts the item at the end of the vector.

// Create an empty PersistentVector and add some elements
PersistentVector<string> vector =
    PersistentVector<string>.Empty()
        .Conj("hello")
        .Conj("world")
        .Conj("!");

Console.WriteLine(vector[0]); // hello
Console.WriteLine(vector[2]); // !            

// Check no. of elements in the PersistentVector
Console.WriteLine(vector.Length);  // 3

PersistentVectors are immutable and therefor allow to create new version without destruction of the old ones.

PersistentVector<string> vector2 = vector.Conj("!!!").Update(0,"hi");

Console.WriteLine(vector2[0]); // hi
Console.WriteLine(vector[0]);  // hello
Console.WriteLine(vector2[3]); // !!!

Console.WriteLine(vector.Length);  // 3
Console.WriteLine(vector2.Length); // 4

// remove the last element from a PersistentVector
PersistentVector<string> vector3 = vector2.Initial;

Console.WriteLine(vector3.Length); // 3

Performance Tests

Bulk operations on PersistentVector use an internal TransientVector in order to get much better performance. The following scripts shows this:

open FSharpx.Collections

let trials = 5
let r = new System.Random()

let initArrayAndVectorFromList n =
    sprintf "Init with n = %d" n |> printInFsiTags
    let list = [for i in 1..n -> r.Next()]

    let initvector list = 
        let v = ref PersistentVector.empty
        for x in list do
            v := PersistentVector.conj x !v
        !v

    averageTime trials "  Array.ofSeq" 
        (fun () -> Array.ofSeq list)

    averageTime trials "  Multiple PersistentVector.conj" 
        (fun () -> initvector list)

    averageTime trials "  PersistentVector.ofSeq" 
        (fun () -> PersistentVector.ofSeq list)

let lookupInArrayAndVector n count =
    sprintf "%d Lookups in size n = %d" count n |> printInFsiTags
    let list = [for i in 1..n -> r.Next()]
    let array = Array.ofSeq list
    let vector = PersistentVector.ofSeq list

    averageTime trials "  Array" 
        (fun () -> for i in 1..count do array.[r.Next n])

    averageTime trials "  PersistentVector" 
        (fun () -> for i in 1..count do PersistentVector.nth (r.Next n) vector)


let replaceInArrayAndVector n count =
    sprintf "%d writes in size n = %d" count n |> printInFsiTags
    let list = [for i in 1..n -> r.Next()]
    let array = Array.ofSeq list
    let vector = PersistentVector.ofSeq list

    averageTime trials "  Array" 
        (fun () -> for i in 1..count do array.[r.Next n] <- r.Next())

    averageTime trials "  PersistentVector" 
        (fun () -> for i in 1..count do PersistentVector.update (r.Next n) (r.Next()) vector)

initArrayAndVectorFromList 10000
initArrayAndVectorFromList 100000
initArrayAndVectorFromList 1000000
[fsi:Init with n = 10000]
 [fsi:  Array.ofSeq 0.0ms]
 [fsi:  Multiple PersistentVector.conj 2.4ms]
 [fsi:  PersistentVector.ofSeq 0.8ms]
 [fsi:Init with n = 100000]
 [fsi:  Array.ofSeq 0.4ms]
 [fsi:  Multiple PersistentVector.conj 26.4ms]
 [fsi:  PersistentVector.ofSeq 9.4ms]
 [fsi:Init with n = 1000000]
 [fsi:  Array.ofSeq 8.2ms]
 [fsi:  Multiple PersistentVector.conj 334.4ms]
 [fsi:  PersistentVector.ofSeq 131.2ms]
lookupInArrayAndVector 10000 10000
lookupInArrayAndVector 100000 10000
lookupInArrayAndVector 1000000 10000
[fsi:10000 Lookups in size n = 10000]
 [fsi:  Array 0.0ms]
 [fsi:  PersistentVector 0.4ms]
 [fsi:10000 Lookups in size n = 100000]
 [fsi:  Array 0.0ms]
 [fsi:  PersistentVector 0.8ms]
 [fsi:10000 Lookups in size n = 1000000]
 [fsi:  Array 0.2ms]
 [fsi:  PersistentVector 2.8ms]
replaceInArrayAndVector 10000 10000
replaceInArrayAndVector 100000 10000
replaceInArrayAndVector 1000000 10000
[fsi:10000 writes in size n = 10000]
 [fsi:  Array 0.2ms]
 [fsi:  PersistentVector 6.2ms]
 [fsi:10000 writes in size n = 100000]
 [fsi:  Array 0.2ms]
 [fsi:  PersistentVector 10.0ms]
 [fsi:10000 writes in size n = 1000000]
 [fsi:  Array 0.2ms]
 [fsi:  PersistentVector 9.4ms]
namespace System
namespace FSharpx
namespace FSharpx.Collections
val v : PersistentVector<string>
Multiple items
module PersistentVector from FSharpx.Collections
<summary> Defines functions which allow to access and manipulate PersistentVectors. </summary>

--------------------
type PersistentVector<'T> = interface IReadOnlyCollection<'T> interface IEnumerable<'T> interface IEnumerable member Conj : 'T -> PersistentVector<'T> member Rev : unit -> PersistentVector<'T> member TryUpdate : int * 'T -> PersistentVector<'T> option member Update : int * 'T -> PersistentVector<'T> static member Empty : unit -> PersistentVector<'T> member Initial : PersistentVector<'T> member IsEmpty : bool ...
<summary> PersistentVector is an ordered linear structure implementing the inverse of the List signature, (last, initial, conj) in place of (head, tail, cons). Length is O(1). Indexed lookup or update (returning a new immutable instance of Vector) of any element is O(log32n), which is close enough to O(1) as to make no practical difference: a PersistentVector containing 4 billion items can lookup or update any item in at most 7 steps. Ordering is by insertion history. The original idea can be found in [Clojure](http://clojure.org/data_structures). </summary>
val empty<'T> : PersistentVector<'T>
<summary> O(1). Returns vector of no elements. </summary>
val conj : 'T -> PersistentVector<'T> -> PersistentVector<'T>
<summary> O(1). Returns a new vector with the element added at the end. </summary>
val nth : int -> PersistentVector<'T> -> 'T
<summary> 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. </summary>
val length : PersistentVector<'T> -> int
<summary> O(1). Returns the number of items in the vector. </summary>
val v' : PersistentVector<string>
val update : int -> 'T -> PersistentVector<'T> -> PersistentVector<'T>
<summary> O(1) for all practical purposes; really O(log32n). Returns a new vector that contains the given value at the index. </summary>
val v'' : PersistentVector<string>
val initial : PersistentVector<'T> -> PersistentVector<'T>
<summary> O(1) for all practical purposes; really O(log32n). Returns a new vector without the last item. If the collection is empty it throws an exception. </summary>
val intVector : PersistentVector<int>
val ofSeq : seq<'T> -> PersistentVector<'T>
<summary> O(n). Returns a vector of the seq. </summary>
val intVector' : PersistentVector<int>
val map : ('T -> 'T1) -> PersistentVector<'T> -> PersistentVector<'T1>
<summary> O(n). Returns a vector whose elements are the results of applying the supplied function to each of the elements of a supplied vector. </summary>
val x : int
val stopTime : f:(unit -> 'a) -> 'a * float
 Stops the runtime for a given function
val f : (unit -> 'a)
val sw : Diagnostics.Stopwatch
namespace System.Diagnostics
Multiple items
type Stopwatch = new : unit -> unit member Reset : unit -> unit member Restart : unit -> unit member Start : unit -> unit member Stop : unit -> unit static member GetTimestamp : unit -> int64 static member StartNew : unit -> Stopwatch static val Frequency : int64 static val IsHighResolution : bool member Elapsed : TimeSpan ...
<summary>Provides a set of methods and properties that you can use to accurately measure elapsed time.</summary>

--------------------
Diagnostics.Stopwatch() : Diagnostics.Stopwatch
Diagnostics.Stopwatch.Start() : unit
val result : 'a
Diagnostics.Stopwatch.Stop() : unit
Multiple items
val float : value:'T -> float (requires member op_Explicit)
<summary>Converts the argument to 64-bit float. This is a direct conversion for all primitive numeric types. For strings, the input is converted using <c>Double.Parse()</c> with InvariantCulture settings. Otherwise the operation requires an appropriate static conversion method on the input type.</summary>
<param name="value">The input value.</param>
<returns>The converted float</returns>


--------------------
[<Struct>] type float = Double
<summary>An abbreviation for the CLI type <see cref="T:System.Double" />.</summary>
<category>Basic Types</category>


--------------------
type float<'Measure> = float
<summary>The type of double-precision floating point numbers, annotated with a unit of measure. The unit of measure is erased in compiled code and when values of this type are analyzed using reflection. The type is representationally equivalent to <see cref="T:System.Double" />.</summary>
<category index="6">Basic Types with Units of Measure</category>
property Diagnostics.Stopwatch.ElapsedMilliseconds: int64 with get
<summary>Gets the total elapsed time measured by the current instance, in milliseconds.</summary>
<returns>A read-only long integer representing the total number of milliseconds measured by the current instance.</returns>
val stopAverageTime : count:int -> f:(unit -> 'a) -> float
 Stops the average runtime for a given function and applies it the given count
val count : int
type GC = static member AddMemoryPressure : bytesAllocated: int64 -> unit static member AllocateArray<'T> : length: int *?pinned: bool -> 'T [] static member AllocateUninitializedArray<'T> : length: int *?pinned: bool -> 'T [] static member CancelFullGCNotification : unit -> unit static member Collect : unit -> unit + 4 overloads static member CollectionCount : generation: int -> int static member EndNoGCRegion : unit -> unit static member GetAllocatedBytesForCurrentThread : unit -> int64 static member GetGCMemoryInfo : unit -> GCMemoryInfo + 1 overload static member GetGeneration : obj: obj -> int + 1 overload ...
<summary>Controls the system garbage collector, a service that automatically reclaims unused memory.</summary>
GC.Collect() : unit
GC.Collect(generation: int) : unit
GC.Collect(generation: int, mode: GCCollectionMode) : unit
GC.Collect(generation: int, mode: GCCollectionMode, blocking: bool) : unit
GC.Collect(generation: int, mode: GCCollectionMode, blocking: bool, compacting: bool) : unit
val ignore : value:'T -> unit
<summary>Ignore the passed value. This is often used to throw away results of a computation.</summary>
<param name="value">The value to ignore.</param>
val printInFsiTags : s:string -> unit
val s : string
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
<summary>Print to <c>stdout</c> using the given format, and add a newline.</summary>
<param name="format">The formatter.</param>
<returns>The formatted result.</returns>
val averageTime : count:int -> desc:string -> f:(unit -> 'a) -> unit
 Stops the average runtime for a given function and applies it the given count
 Afterwards it reports it with the given description
val desc : string
val time : float
val sprintf : format:Printf.StringFormat<'T> -> 'T
<summary>Print to a string using the given format.</summary>
<param name="format">The formatter.</param>
<returns>The formatted result.</returns>
val trials : int
val r : Random
Multiple items
type Random = new : unit -> unit + 1 overload member Next : unit -> int + 2 overloads member NextBytes : buffer: byte [] -> unit + 1 overload member NextDouble : unit -> float member Sample : unit -> float
<summary>Represents a pseudo-random number generator, which is an algorithm that produces a sequence of numbers that meet certain statistical requirements for randomness.</summary>

--------------------
Random() : Random
Random(Seed: int) : Random
val initArrayAndVectorFromList : n:int -> unit
val n : int
Multiple items
val list : int list

--------------------
type 'T list = List<'T>
<summary>The type of immutable singly-linked lists. </summary>
<remarks>See the <see cref="T:Microsoft.FSharp.Collections.ListModule" /> module for further operations related to lists. Use the constructors <c>[]</c> and <c>::</c> (infix) to create values of this type, or the notation <c>[1; 2; 3]</c>. Use the values in the <c>List</c> module to manipulate values of this type, or pattern match against the values directly. See also <a href="https://docs.microsoft.com/dotnet/fsharp/language-reference/lists">F# Language Guide - Lists</a>. </remarks>
val i : int
Random.Next() : int
Random.Next(maxValue: int) : int
Random.Next(minValue: int, maxValue: int) : int
val initvector : (seq<'a> -> PersistentVector<'a>)
Multiple items
val list : seq<'a>

--------------------
type 'T list = List<'T>
<summary>The type of immutable singly-linked lists. </summary>
<remarks>See the <see cref="T:Microsoft.FSharp.Collections.ListModule" /> module for further operations related to lists. Use the constructors <c>[]</c> and <c>::</c> (infix) to create values of this type, or the notation <c>[1; 2; 3]</c>. Use the values in the <c>List</c> module to manipulate values of this type, or pattern match against the values directly. See also <a href="https://docs.microsoft.com/dotnet/fsharp/language-reference/lists">F# Language Guide - Lists</a>. </remarks>
val v : PersistentVector<'a> ref
Multiple items
val ref : value:'T -> 'T ref
<summary>Create a mutable reference cell</summary>
<param name="value">The value to contain in the cell.</param>
<returns>The created reference cell.</returns>


--------------------
type 'T ref = Ref<'T>
<summary>The type of mutable references. Use the functions [!] and [:=] to get and set values of this type.</summary>
<category>Basic Types</category>
val x : 'a
type Array = interface ICollection interface IEnumerable interface IList interface IStructuralComparable interface IStructuralEquatable interface ICloneable new : unit -> unit member Clone : unit -> obj member CopyTo : array: Array * index: int -> unit + 1 overload member GetEnumerator : unit -> IEnumerator ...
<summary>Provides methods for creating, manipulating, searching, and sorting arrays, thereby serving as the base class for all arrays in the common language runtime.</summary>
val ofSeq : source:seq<'T> -> 'T []
<summary>Builds a new array from the given enumerable object.</summary>
<param name="source">The input sequence.</param>
<returns>The array of elements from the sequence.</returns>
<exception cref="T:System.ArgumentNullException">Thrown when the input sequence is null.</exception>
val lookupInArrayAndVector : n:int -> count:int -> unit
Multiple items
val array : int []

--------------------
type 'T array = 'T []
<summary>Single dimensional, zero-based arrays, written <c>int[]</c>, <c>string[]</c> etc.</summary>
<remarks>Use the values in the <see cref="T:Microsoft.FSharp.Collections.ArrayModule" /> module to manipulate values of this type, or the notation <c>arr.[x]</c> to get/set array values.</remarks>
<category>Basic Types</category>
val vector : PersistentVector<int>
val i : int32
val replaceInArrayAndVector : n:int -> count:int -> unit