No language would be complete without arrays (at least in my opinion). They are an excellent structure. F# is of course no fool, and includes them (as you would expect), so this blog will concentrate on looking at using arrays in F#.
Creating Arrays
There are several ways to create arrays, just as there were with List and Sequences. You have the following choices:
- Inline initialisation (where you provide the values)
- Using comprehensions (that we say with List and Sequences)
- Using the Array module itself which has several array creation functions
Here are some examples:
let emptyArray = [| |]
let fruitArray = [| "bananna"; "apple"; "pear"; "mango"; "rambutan"; "lychee" |]
let numberArray = [| 1 .. 10 |]
let onlyLArray = [| for word in fruitArray do
if word.StartsWith("l") then
yield word |]
let generatorArray = Array.init 11 (fun n -> n * 2)
Which when run looks like this:
BEWARE
Now you need to be slightly careful with your syntax (a lot of F# is like this), there is really not much difference between how you declare lists and arrays. See the screen shot below. The key difference is the use of the “[|
” and “|]
”which arrays use and lists do not. One to watch for. We will also see some more quite similar syntax to this further down the line, but that is when we look at passive/active patterns, but we ain't there yet, so let's not cloud the waters with even more syntax for now.
Arrays Of Arrays
We can also make arrays of arrays, which can be done as follows:
let emptyArrayOfArrays = [| [| |] ; [| |] |]
let arrayOfArrays = [| [| 1;2;3|] ; [| 4;5;6|] |]
Which when run looks like this:
Multi Dimensional Arrays
As you would expect, we can also create different dimensioned arrays. There is however no inline syntax (array literal as it is known in F#) for working with 2D arrays (or higher, yes F# supports them up to 4D, if you are mad enough to need it, F# supports it).
We can however use one of the built in F# operators. Here are some examples that show you how to create 2D and 3D arrays:
let array2D = array2D [ [ 1;2]; [2;3]]
let array3DInit = Array3D.init 3 3 3 (fun a b c -> a * 2)
Which when run looks like this:
Everything looks cool with the 2D array, but what is the weird “[|rank=3|]
” gibberish. That is actually just do with the printfn
function and a bit of a red herring if we try and execute the 3D array line in the FSI window, we see the truth.
Which is as expected a 3D array, which is expressed as “int [,,]
”, a 2D int
array would have just been “int [,]
”. The rank simply tells us the dimension count.
As can be seen here with a 4D array (heaven forbid, you need one):
NOTE
It should be noted that the following modules have way less available functions than the rather more common Array F# module:
Indexing Into Arrays
So now that we have seen how to create arrays, we should see how we can get values out of array elements. It is just worth noting that arrays in F# ARE 0 index based.
Here are some examples, showing how to index into single dimension arrays, and arrays of arrays, and a 2D array.
let singleArray = [| 1;2;3;4;5;6 |]
let arrayOfArrays = [| [| 1;2;3|] ; [| 4;5;6|] |]
let array2D = array2D [ [ 1;2;3;4;5]; [11;12;13;14;15]]
printfn "singleArray element [0] = %A" singleArray.[2]
printfn "\r\n"
printfn "arrayOfArrays element [1] [2] = %A" arrayOfArrays.[1].[2]
printfn "\r\n"
printfn "array2D element [1,1] = %A" array2D.[1,1]
Which when run looks like this:
See how we need to index into arrays using the “.[0]
” (where 0
is the index
” syntax. This also applies for arrays of arrays. However for multidimensional arrays, we use the “[1,1]
” syntax.
Mutating Array Element State
So now that you have seen how to index into arrays, you are probably wondering how you can change the state of an array element. In F#, this is done with the assignment operator “<-
“.
Here are some examples, showing how to assign new values into single dimension arrays, and arrays of arrays, and a 2D array.
let singleArray = [| 1;2;3;4;5;6 |]
let arrayOfArrays = [| [| 1;2;3|] ; [| 4;5;6|] |]
let array2D = array2D [ [ 1;2;3;4;5]; [11;12;13;14;15]]
printfn "BEFORE ASSINMENT : singleArray element [0] = %A" singleArray.[2]
printfn "Assigning 999"
singleArray.[2] <- 999
printfn "AFTER ASSINMENT : singleArray element [0] = %A" singleArray.[2]
printfn "\r\n"
printfn "BEFORE ASSINMENT : arrayOfArrays element [1] [2] = %A" arrayOfArrays.[1].[2]
printfn "Assigning 423"
arrayOfArrays.[1].[2] <- 423
printfn "AFTER ASSINMENT : arrayOfArrays element [1] [2] = %A" arrayOfArrays.[1].[2]
printfn "\r\n"
printfn "BEFORE ASSINMENT : array2D element [1,1] = %A" array2D.[1,1]
printfn "Assigning 8989"
array2D.[1,1] <- 8989
printfn "AFTER ASSINMENT : array2D element [1,1] = %A" array2D.[1,1]
Which when run will give the following results:
Slicing Arrays
Sometimes, you may also want to slice arrays to only grab a certain section of the original array. F# has built in support for this, it is called an array slice.
You may choose to slice an original array like this:
- Specify both lower and upper bound, which will copy only those elements between your lower/upper bounds
- Specify lower bound, which will copy only those elements above your lower bound
- Specify upper bound, which will copy only those elements below your upper bond
- Specify wild card, which will in effect copy the entire array
Here is how it is done, in practice:
let arrayof10 = [|1..10|]
let arraySlice0To2 = arrayof10.[0..2]
let arraySlice4ToEnd = arrayof10.[4..]
let arraySliceUntil6 = arrayof10.[..6]
let arrayClone = arrayof10.[*]
printfn "arrayof10 = %A" arrayof10
printfn "\r\n"
printfn "arraySlice0To2 = %A" arraySlice0To2
printfn "\r\n"
printfn "arraySlice4ToEnd = %A" arraySlice4ToEnd
printfn "\r\n"
printfn "arraySliceUntil6 = %A" arraySliceUntil6
printfn "\r\n"
printfn "arrayClone = %A" arrayClone
Which when run, gives the following results:
The Array Module
As with List and Sequence, Arrays are a very important type in F#, as such it should be no surprise that they too have good MSDN documentation. As such, just as I have stated with List and Sequences, it would be fruitless me just regurgitating what is already available on MSDN. There is no point to that.
The F# Array module MSDN documentation can be found here.
I think it is always nice to see a few examples, though, so in fitting with what I have been so far in this series, I will show a few demos, but you should refer to MSDN for the full list of the Array module functions.
I have picked a few random functions which I think are cool, and useful, that said the F# Array module holds many fascinating and useful functions.
Find
Returns the first element for which the given function returns true
. Raise KeyNotFoundException if no such element exists.
FindIndex
Returns the index of the first element in an array that satisfies the supplied condition. Raises KeyNotFoundException if none of the elements satisfy the condition.
Here is a small demo that covers both Find
, and FindIndex
, where we try and Find
the element value when the element contains 2
, and also its index.
let arrayof10 = [|1..10|]
let element = Array.find (fun elem -> elem = 3) arrayof10
let index = Array.findIndex (fun elem -> elem = 3) arrayof10
printfn "The first element that is 3 is %d and its index is %d." element index
Which when run looks like this:
ForAll
Tests whether all elements of an array satisfy the supplied condition.
Here is a small sample that wants all the array elements to be > 0
.
let allAbove0 = (Array.forall (fun elem -> elem > 0) [| 0; 1; 2; 3 |])
printfn "(Array.forall (fun elem -> elem > 0) [| 0; 1; 2; 3 |]) = %A" allAbove0
let allAbove0 = (Array.forall (fun elem -> elem > 0) [| 1; 2; 3 |])
printfn "(Array.forall (fun elem -> elem > 0) [| 1; 2; 3 |]) %A" allAbove0
Which when run produces the following results:
We could also rewrite this use partial application (which is a F# topic I have not covered just yet), but essentially it allows us to partially apply the parameters to a function, then bind that function, and then at some later point in time, supply the remaining parameters. Which in the example below, would be the final parameter (the array itself).
let allPositive = Array.forall (fun elem -> elem > 0)
let allAbove0 = (allPositive [| 0; 1; 2; 3 |])
printfn "(allPositive [| 0; 1; 2; 3 |]) = %A" allAbove0
let allPositive = Array.forall (fun elem -> elem > 0)
let allAbove0 = (allPositive [| 1; 2; 3 |])
printfn "(allPositive [| 1; 2; 3 |]) = %A" allAbove0
Init
Creates an array given the dimension and a generator function to compute the elements.
As you can imagine this is especially useful when you want to initial an array with your own choice of seed data. Here is an example where we fill the entire array with the integer value 5
(God knows why you would want a whole array of 5
s but meh).
printfn "(Array.init 10 (fun index -> 5))"
let array = (Array.init 10 (fun index -> 5))
printfn "%A" array
Which when run, produces the following results:
Partition
Splits the collection into two collections, containing the elements for which the given predicate returns true
and false
respectively.
Here is a small example where we want the results partitioned based on whether the array elements are > 3
or not. So we would expect to get a tuple returned with 2 arrays in it, where one array held elements whose values are < 3
, and second tuple value should be an array where the array element values are >= 3
.
let arrayOf10 = [|0..10|]
let tupleResults = Array.partition (fun element -> element < 3) arrayOf10
printfn "let arrayOf10 = [|0..10|]"
printfn "let tupleResults = Array.partition (fun element -> element < 3) arrayOf10"
printfn "tupleResults fst = %A" (tupleResults |> fst)
printfn "tupleResults snd = %A" (tupleResults |> snd)
Which when run produces the following results: