F# Collections 1
Array
Arrays are fixed-size, zero-based, mutable collections of consecutive data elements that are all of the same type.
Create array
//
let array1 = [| 1; 2; 3 |]
// Put each element on a separate line, in which case the semicolon separator is optional.
let array1 =
[|
1
2
3
|]
// The type of the array elements is inferred from the literals used and must be consistent.
// The following code causes an error because 1.0 is a float and 2 and 3 are integers.
// Causes an error.
// let array2 = [| 1.0; 2; 3 |]
// Use sequence expressions to create arrays.
let array3 = [| for i in 1 .. 10 -> i * i |]
// use Array.zeroCreate
let arrayOfTenZeroes : int array = Array.zeroCreate 10
// use Array.empty
let myEmptyArray = Array.empty
printfn "Length of empty array: %d" myEmptyArray.Length
// Length of empty array: 0
// use Array.create
printfn "Array of floats set to 5.0: %A" (Array.create 10 5.0)
// Area of floats set to 5.0: [|5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0; 5.0|]
// Array.init
printfn "Array of squares: %A" (Array.init 10 (fun index -> index * index))
// Array of squares: [|0; 1; 4; 9; 16; 25; 36; 49; 64; 81|]
Copy
open System.Text
let firstArray : StringBuilder array = Array.init 3 (fun index -> new StringBuilder(""))
let secondArray = Array.copy firstArray
// Reset an element of the first array to a new value.
firstArray[0] <- new StringBuilder("Test1")
// Change an element of the first array.
firstArray[1].Insert(0, "Test2") |> ignore
printfn "%A" firstArray
printfn "%A" secondArray
// output
// [|Test1; Test2; |]
// [|; Test2; |]
Access
// Accesses elements from 0 to 2.
array1[0..2]
// Accesses elements from the beginning of the array to 2.
array1[..2]
// Accesses elements from 2 to the end of the array.
array1[2..]
Functions
// Array.sub
let a1 = [| 0 .. 99 |]
let a2 = Array.sub a1 5 10
printfn "%A" a2
// output
// [|5; 6; 7; 8; 9; 10; 11; 12; 13; 14|]
// Array.append creates a new array by combining two existing arrays.
printfn "%A" (Array.append [| 1; 2; 3|] [| 4; 5; 6|])
// output
// [|1; 2; 3; 4; 5; 6|]
// Array.choose selects elements of an array to include in a new array.
printfn "%A" (Array.choose (fun elem -> if elem % 2 = 0 then
Some(float (elem*elem - 1))
else
None) [| 1 .. 10 |])
// output
// [|3.0; 15.0; 35.0; 63.0; 99.0|]
// Array.collect runs a specified function on each array element of an existing array and then collects the elements generated by the function and combines them into a new array.
printfn "%A" (Array.collect (fun elem -> [| 0 .. elem |]) [| 1; 5; 10|])
// output
// [|0; 1; 0; 1; 2; 3; 4; 5; 0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
Array.concat takes a sequence of arrays and combines them into a single array. The following code demonstrates Array.concat.
F#
Copy
Array.concat [ [|0..3|] ; [|4|] ]
//output [|0; 1; 2; 3; 4|]
Array.concat [| [|0..3|] ; [|4|] |]
//output [|0; 1; 2; 3; 4|]
// Array.filter takes a Boolean condition function and generates a new array that contains only those elements from the input array for which the condition is true.
printfn "%A" (Array.filter (fun elem -> elem % 2 = 0) [| 1 .. 10|])
// The output
// [|2; 4; 6; 8; 10|]
// Array.rev generates a new array by reversing the order of an existing array.
let stringReverse (s: string) =
System.String(Array.rev (s.ToCharArray()))
printfn "%A" (stringReverse("!dlrow olleH"))
// The output
// "Hello world!"
// You can easily combine functions in the array module that transform arrays by using the pipeline operator (|>)
[| 1 .. 10 |]
|> Array.filter (fun elem -> elem % 2 = 0)
|> Array.choose (fun elem -> if (elem <> 8) then Some(elem*elem) else None)
|> Array.rev
|> printfn "%A"
// output
// [|100; 36; 16; 4|]
Multidimensional arrays
let my2DArray = array2D [ [ 1; 0]; [0; 1] ]
let arrayOfArrays = [| [| 1.0; 0.0 |]; [|0.0; 1.0 |] |]
let twoDimensionalArray = Array2D.init 2 2 (fun i j -> arrayOfArrays[i][j])
Slicing
// Get rows 1 to N from an NxM matrix (returns a matrix):
matrix[1.., *]
// Get rows 1 to 3 from a matrix (returns a matrix):
matrix[1..3, *]
// Get columns 1 to 3 from a matrix (returns a matrix):
matrix[*, 1..3]
// Get a 3x3 submatrix:
matrix[1..3, 1..3]
// Get row 3 from a matrix as a vector:
matrix[3, *]
// Get column 3 from a matrix as a vector:
matrix[*, 3]
Matrix & slicing
type Matrix<'T>(N: int, M: int) =
let internalArray = Array2D.zeroCreate<'T> N M
member this.Item
with get(a: int, b: int) = internalArray[a, b]
and set(a: int, b: int) (value:'T) = internalArray[a, b] <- value
member this.GetSlice(rowStart: int option, rowFinish : int option, colStart: int option, colFinish : int option) =
let rowStart =
match rowStart with
| Some(v) -> v
| None -> 0
let rowFinish =
match rowFinish with
| Some(v) -> v
| None -> internalArray.GetLength(0) - 1
let colStart =
match colStart with
| Some(v) -> v
| None -> 0
let colFinish =
match colFinish with
| Some(v) -> v
| None -> internalArray.GetLength(1) - 1
internalArray[rowStart..rowFinish, colStart..colFinish]
member this.GetSlice(row: int, colStart: int option, colFinish: int option) =
let colStart =
match colStart with
| Some(v) -> v
| None -> 0
let colFinish =
match colFinish with
| Some(v) -> v
| None -> internalArray.GetLength(1) - 1
internalArray[row, colStart..colFinish]
member this.GetSlice(rowStart: int option, rowFinish: int option, col: int) =
let rowStart =
match rowStart with
| Some(v) -> v
| None -> 0
let rowFinish =
match rowFinish with
| Some(v) -> v
| None -> internalArray.GetLength(0) - 1
internalArray[rowStart..rowFinish, col]
module test =
let generateTestMatrix x y =
let matrix = new Matrix<float>(3, 3)
for i in 0..2 do
for j in 0..2 do
matrix[i, j] <- float(i) * x - float(j) * y
matrix
let test1 = generateTestMatrix 2.3 1.1
let submatrix = test1[0..1, 0..1]
printfn $"{submatrix}"
let firstRow = test1[0,*]
let secondRow = test1[1,*]
let firstCol = test1[*,0]
printfn $"{firstCol}"
Search
// Array.find takes a Boolean function and returns the first element for which
// the function returns true, or raises a
// System.Collections.Generic.KeyNotFoundException if no element that satisfies
// the conditionis found.
// Array.findIndex is like Array.find, except that it returns the index of
// the element instead of the element itself.
// The following code uses Array.find and Array.findIndex to locate a number that
// is both a perfect square and perfect cube.
let arrayA = [| 2 .. 100 |]
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
let element = Array.find (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
let index = Array.findIndex (fun elem -> isPerfectSquare elem && isPerfectCube elem) arrayA
printfn "The first element that is both a square and a cube is %d and its index is %d." element index
// The output is as follows.
// The first element that is both a square and a cube is 64 and its index is 62.
// ----------------------------------------------------------------------------
// Array.tryFind is like Array.find, except that its result is an option type,
// and it returns None if no element is found. Array.tryFind should be used
// instead of Array.find when you do not know whether a matching element is
// in the array. Similarly, Array.tryFindIndex is like Array.findIndex except
// that the option type is the return value. If no element is found, the option is None.
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
let lookForCubeAndSquare array1 =
let result = Array.tryFind (fun elem -> isPerfectSquare elem && isPerfectCube elem) array1
match result with
| Some x -> printfn "Found an element: %d" x
| None -> printfn "Failed to find a matching element."
lookForCubeAndSquare [| 1 .. 10 |]
lookForCubeAndSquare [| 100 .. 1000 |]
lookForCubeAndSquare [| 2 .. 50 |]
// The output is as follows.
// Found an element: 1
// Found an element: 729
// Failed to find a matching element.
// ----------------------------------------------------------------------------
// Use Array.tryPick when you need to transform an element in addition to finding it.
// The result is the first element for which the function returns the transformed
// element as an option value, or None if no such element is found.
// The following code shows the use of Array.tryPick. In this case, instead of
// a lambda expression, several local helper functions are defined to simplify the code.
let findPerfectSquareAndCube array1 =
let delta = 1.0e-10
let isPerfectSquare (x:int) =
let y = sqrt (float x)
abs(y - round y) < delta
let isPerfectCube (x:int) =
let y = System.Math.Pow(float x, 1.0/3.0)
abs(y - round y) < delta
// intFunction : (float -> float) -> int -> int
// Allows the use of a floating point function with integers.
let intFunction function1 number = int (round (function1 (float number)))
let cubeRoot x = System.Math.Pow(x, 1.0/3.0)
// testElement: int -> (int * int * int) option
// Test an element to see whether it is a perfect square and a perfect
// cube, and, if so, return the element, square root, and cube root
// as an option value. Otherwise, return None.
let testElement elem =
if isPerfectSquare elem && isPerfectCube elem then
Some(elem, intFunction sqrt elem, intFunction cubeRoot elem)
else None
match Array.tryPick testElement array1 with
| Some (n, sqrt, cuberoot) -> printfn "Found an element %d with square root %d and cube root %d." n sqrt cuberoot
| None -> printfn "Did not find an element that is both a perfect square and a perfect cube."
findPerfectSquareAndCube [| 1 .. 10 |]
findPerfectSquareAndCube [| 2 .. 100 |]
findPerfectSquareAndCube [| 100 .. 1000 |]
findPerfectSquareAndCube [| 1000 .. 10000 |]
findPerfectSquareAndCube [| 2 .. 50 |]
// The output is as follows.
// Found an element 1 with square root 1 and cube root 1.
// Found an element 64 with square root 8 and cube root 4.
// Found an element 729 with square root 27 and cube root 9.
// Found an element 4096 with square root 64 and cube root 16.
// Did not find an element that is both a perfect square and a perfect cube.
Modify arrays
Array.set sets an element to a specified value. Array.fill sets a range of elements in an array to a specified value. The following code provides an example of Array.fill.
let arrayFill1 = [| 1 .. 25 |]
Array.fill arrayFill1 2 20 0
printfn "%A" arrayFill1
// output
// [|1; 2; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 23; 24; 25|]
Convert to and from other types
Array.ofList creates an array from a list. Array.ofSeq creates an array from a sequence. Array.toList and Array.toSeq convert to these other collection types from the array type.