List. In an F# list we can store strings and ints. We can make a list of any element type. But after we create a list, it cannot be changed—it is immutable.
By accessing the Head and Tail, we can keep track of which elements we have processed. We can access other important properties like Length on lists.
Head and Tail. To start, we create an example string list. We access the Length of the list, which is 3. We then use the Head and Tail properties, which are important on the list.
Info Head is the first element within the list. It is not a list itself. It is just an element.
Next Tail is another list, but it does not include the first element. It is the remainder of the list.
// Create a list of strings with animal names.// ... Use semicolon to separate elements, not comma.
let continents = ["Antarctica"; "Asia"; "Africa"]
// It has three elements.
let count = continents.Length
printfn "%A" count
// Head is the first element (a string here).
let head = continents.Head
printfn "%A" head
// Tail is the list except for the first element.// ... It includes the second element onwards.
let tail = continents.Tail
printfn "%A" tail3
For-loop. Let us create a list of 4 strings. Please note that the semicolon is used to separate elements in the list. A comma will create tuples within the list.
Detail We use a for-in, do-loop to access all the elements in the list. Each "animal" is a string. We use printfn to print these values.
// Create a list of strings with animal names.
let animals = ["cat"; "bird"; "fish"; "fox"]
// Use loop to print all animals.for animal in animals do
printfn "%A" animal"cat"
Recursion, match. This is a more complex example. We introduce a recursive method printExample. The rec keyword indicates it is a recursive (self-calling) method.
Detail The match block must handle all cases of the list when evaluated. The list can be empty, or have a head and a tail.
Also The "head" case in the match-with statement handles a nonempty list. It prints the head and then recurses on the tail.
So We repeatedly call the method with the tail as the argument. The tail keeps shrinking. The head is printed each time.
// Use recursion to print a list.// ... Match handles all cases of the list.// We handle the head and tail by printing the head.// We use recursion on the tail for the next elements.letrec printExample list =
match list with
|  -> ()
| head :: tail ->
printf "%d " head
// Test our recursive method.
let numbers = [5; 10; 15; 20]
printExample numbers5 10 15 20
List.append. An F# list cannot be appended to in-place. Instead, we can use List.append to combine two lists together into a third list.
Tip To use List.append, we must have two lists. But we can create a one-element list to append just one element.
let colors1 = ["blue"; "orange"]
// Combine the two lists.// ... We append a 1-element list to append an element.
let colors2 = List.append colors1 ["pink"]
// Display before and after lists.
printfn "%A" colors1
printfn "%A" colors2["blue"; "orange"]
["blue"; "orange"; "pink"]
Concatenate operator. With the "at" symbol we concatenate (combine) two lists. This is the same as the List.append function, but with shorter syntax.
Here We add an element at the start, and then add one at the end of the list. A new list is created each time.
let materials = ["plastic"; "wood"]
printfn "%A" materials
// Add element to the beginning of the list.let materials2 = ["cement"] @ materials
printfn "%A" materials2
// Add element to the end of the list.let materials3 = materials2 @ ["brick"]
printfn "%A" materials3["plastic"; "wood"]
["cement"; "plastic"; "wood"]
["cement"; "plastic"; "wood"; "brick"]
Add to front. With two ":" characters we add to the front of a list. This is a clearer syntax than using "at" or List.append for this operation.
// Create a list with two elements.let lengths = [10; 10]
printfn "%A" lengths
// Add a value to the front.let lengths2 = 5 :: lengths
printfn "%A" lengths2
// Add a new front.let lengths3 = 0 :: lengths2
printfn "%A" lengths3[10; 10]
[5; 10; 10]
[0; 5; 10; 10]
Argument, map, sort. This example is more complex. It introduces a sortDouble function that receives a list argument. It then pipelines that list through List.map and List.sort.
Note In sortDouble we receive an argument of name "x." We pass this list argument to List.map.
Detail List.map() uses a lambda expression, specified with "fun," to double each element (with identifier "y").
Finally Sort() is called after List.map with that function's result. This sorts the doubled list in an ascending order (low to high).
// This function doubles all elements in a list.// ... It then sorts them.
let sortDouble x =
|> List.map (fun y -> y * 2)
let items = [20; 10; 5; 50]
// Double and sort our list elements.
let result = sortDouble items
printfn "%A" result[10; 20; 40; 100]
ResizeArray. With F# we can access all .NET types. The System.Collections.Generic.List is often used. And F# provides a handy alias for it called ResizeArray.
Note The ResizeArray underscore means a type is chosen automatically. We use Seq.toList to convert to an F# list.
// Create a System.Collections.Generic.List.// ... The ResizeArray is an aliased type.
let resize = new ResizeArray<_>()
// Convert our ResizeArray to a list.
let result = Seq.toList resize
// Display our list.
printfn "%A" result.Length
printfn "%A" result.Head3
Some notes. Imagine a method acts on the first element of a list, and then must process the remainder of the list in later calls. We can just process Head, and use Tail to eliminate Head.
So With Head and Tail we can reduce the list as we use it. This keeps track of our position within the list.
A review. With lists, we collect elements into groups. We can use higher-order procedures to act upon these lists. And Head and Tail help us reduce lists as we proceed.