F# - Lists

In F#, a list is an ordered, immutable series of elements of the same type. It is to some extent equivalent to a linked list data structure.
The F# module, Microsoft.FSharp.Collections.List, has the common operations on lists. However F# imports this module automatically and makes it accessible to every F# application.
Following are the various ways of creating lists −
Using list literals.
Using list literals.
Using cons (::) operator.
Using cons (::) operator.
Using the List.init method of List module.
Using the List.init method of List module.
Using some syntactic constructs called List Comprehensions.
Using some syntactic constructs called List Comprehensions.
In this method, you just specify a semicolon-delimited sequence of values in square brackets. For example −
let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]

With this method, you can add some values by prepending or cons-ing it to an existing list using the :: operator. For example −
let list2 = 1::2::3::4::5::6::7::8::9::10::[];;

[] denotes an empty list.
The List.init method of the List module is often used for creating lists. This method has the type −
val init : int -> (int -> 'T) -> 'T list

The first argument is the desired length of the new list, and the second argument is an initializer function, which generates items in the list.
For example,
let list5 = List.init 5 (fun index -> (index, index * index, index * index * index))

Here, the index function generates the list.
List comprehensions are special syntactic constructs used for generating lists.
F# list comprehension syntax comes in two forms − ranges and generators.
Ranges have the constructs − [start .. end] and [start .. step .. end]
For example,
let list3 = [1 .. 10]

Generators have the construct − [for x in collection do ... yield expr]
For example,
let list6 = [ for a in 1 .. 10 do yield (a * a) ]

As the yield keyword pushes a single value into a list, the keyword, yield!, pushes a collection of values into the list.
The following function demonstrates the above methods −
(* using list literals *)
let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
printfn "The list: %A" list1

(*using cons operator *)
let list2 = 1 :: 2 :: 3 :: []
printfn "The list: %A" list2

(* using range constructs*)
let list3 = [1 .. 10]
printfn "The list: %A" list3

(* using range constructs *)
let list4 = ['a' .. 'm']
printfn "The list: %A" list4

(* using init method *)
let list5 = List.init 5 (fun index -> (index, index * index, index * index * index))
printfn "The list: %A" list5

(* using yield operator *)
let list6 = [ for a in 1 .. 10 do yield (a * a) ]
printfn "The list: %A" list6

(* using yield operator *)
let list7 = [ for a in 1 .. 100 do if a % 3 = 0 && a % 5 = 0 then yield a]
printfn "The list: %A" list7

(* using yield! operator *)
let list8 = [for a in 1 .. 3 do yield! [ a .. a + 3 ] ]
printfn "The list: %A" list8
When you compile and execute the program, it yields the following output −
The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The list: [1; 2; 3]
The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The list: ['a'; 'b'; 'c'; 'd'; 'e'; 'f'; 'g'; 'h'; 'i'; 'j'; 'k'; 'l'; 'm']
The list: [(0, 0, 0); (1, 1, 1); (2, 4, 8); (3, 9, 27); (4, 16, 64)]
The list: [1; 4; 9; 16; 25; 36; 49; 64; 81; 100]
The list: [15; 30; 45; 60; 75; 90]
The list: [1; 2; 3; 4; 2; 3; 4; 5; 3; 4; 5; 6]

The following table shows various properties of list data type −
The following example shows the use of these properties −
let list1 = [ 2; 4; 6; 8; 10; 12; 14; 16 ]

// Use of Properties
printfn "list1.IsEmpty is %b" (list1.IsEmpty)
printfn "list1.Length is %d" (list1.Length)
printfn "list1.Head is %d" (list1.Head)
printfn "list1.Tail.Head is %d" (list1.Tail.Head)
printfn "list1.Tail.Tail.Head is %d" (list1.Tail.Tail.Head)
printfn "list1.Item(1) is %d" (list1.Item(1))
When you compile and execute the program, it yields the following output −
list1.IsEmpty is false
list1.Length is 8
list1.Head is 2
list1.Tail.Head is 4
list1.Tail.Tail.Head is 6
list1.Item(1) is 4

The following table shows the basic operations on list data type −
The following examples demonstrate the uses of the above functionalities −
This program shows reversing a list recursively −
let list1 = [ 2; 4; 6; 8; 10; 12; 14; 16 ]
printfn "The original list: %A" list1

let reverse lt =
   let rec loop acc = function
      | [] -> acc
      | hd :: tl -> loop (hd :: acc) tl
   loop [] lt

printfn "The reversed list: %A" (reverse list1)
When you compile and execute the program, it yields the following output −
The original list: [2; 4; 6; 8; 10; 12; 14; 16]
The reversed list: [16; 14; 12; 10; 8; 6; 4; 2]

However, you can use the rev function of the module for the same purpose −
let list1 = [ 2; 4; 6; 8; 10; 12; 14; 16 ]
printfn "The original list: %A" list1
printfn "The reversed list: %A" (List.rev list1)
When you compile and execute the program, it yields the following output −
The original list: [2; 4; 6; 8; 10; 12; 14; 16]
The reversed list: [16; 14; 12; 10; 8; 6; 4; 2]

This program shows filtering a list using the List.filter method −
let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
printfn "The list: %A" list1
let list2 = list1 |> List.filter (fun x -> x % 2 = 0);;
printfn "The Filtered list: %A" list2
When you compile and execute the program, it yields the following output −
The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The Filtered list: [2; 4; 6; 8; 10]

The List.map method maps a list from one type to another −
let list1 = [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
printfn "The list: %A" list1
let list2 = list1 |> List.map (fun x -> (x * x).ToString());;
printfn "The Mapped list: %A" list2
When you compile and execute the program, it yields the following output −
The list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The Mapped list: ["1"; "4"; "9"; "16"; "25"; "36"; "49"; "64"; "81"; "100"]

The List.append method and the @ operator appends one list to another −
let list1 = [1; 2; 3; 4; 5 ]
let list2 = [6; 7; 8; 9; 10]
let list3 = List.append list1 list2

printfn "The first list: %A" list1
printfn "The second list: %A" list2
printfn "The appened list: %A" list3

let lt1 = ['a'; 'b';'c' ]
let lt2 = ['e'; 'f';'g' ]
let lt3 = lt1 @ lt2

printfn "The first list: %A" lt1
printfn "The second list: %A" lt2
printfn "The appened list: %A" lt3
When you compile and execute the program, it yields the following output −
The first list: [1; 2; 3; 4; 5]
The second list: [6; 7; 8; 9; 10]
The appened list: [1; 2; 3; 4; 5; 6; 7; 8; 9; 10]
The first list: ['a'; 'b'; 'c']
The second list: ['e'; 'f'; 'g']
The appened list: ['a'; 'b'; 'c'; 'e'; 'f'; 'g']

The List.sort method sorts a list. The List.sum method gives the sum of elements in the list and the List.average method gives the average of elements in the list −
let list1 = [9.0; 0.0; 2.0; -4.5; 11.2; 8.0; -10.0]
printfn "The list: %A" list1

let list2 = List.sort list1
printfn "The sorted list: %A" list2

let s = List.sum list1
let avg = List.average list1
printfn "The sum: %f" s
printfn "The average: %f" avg
When you compile and execute the program, it yields the following output −
The list: [9.0; 0.0; 2.0; -4.5; 11.2; 8.0; -10.0]
The sorted list: [-10.0; -4.5; 0.0; 2.0; 8.0; 9.0; 11.2]
The sum: 15.700000
The average: 2.242857

A "fold" operation applies a function to each element in a list, aggregates the result of the function in an accumulator variable, and returns the accumulator as the result of the fold operation.
The List.fold method applies a function to each element from left to right, while List.foldBack applies a function to each element from right to left.
let sumList list = List.fold (fun acc elem -> acc + elem) 0 list
printfn "Sum of the elements of list %A is %d." [ 1 .. 10 ] (sumList [ 1 .. 10 ])
When you compile and execute the program, it yields the following output −
Sum of the elements of list [1; 2; 3; 4; 5; 6; 7; 8; 9; 10] is 55.

 Print
 Add Notes
 Bookmark this page