F Sharp Programming/Lists

A list is an ordered collection of related values, and is roughly equivalent to a linked list data structure used in many other languages. F# provides a module,, for common operations on lists; this module is imported automatically by F#, so the   module is already accessible from every F# application.

Using List Literals
There are a variety of ways to create lists in F#, the most straightforward method being a semicolon-delimited sequence of values. Here's a list of numbers in fsi:

Notice that all values in a list must have the same type:

Using the ("cons") Operator
It is very common to build lists up by prepending or consing a value to an existing list using the  operator:


 * Note: the  is an empty list. By itself, it has the type  ; since it is used with  s, it has the type.

The  operator prepends items to a list, returning a new list. It is a right-associative operator with the following type:

This operator does not actually mutate lists, it creates an entirely new list with the prepended element in the front. Here's an example in fsi:

Consing creates a new list, but it reuses nodes from the old list, so consing a list is an extremely efficient O(1) operation.

Using List.init
The  module contains a useful method, , which has the type

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. is used as follows:

F# calls the initializer function  times with the index of each item in the list, starting at index.

Using List Comprehensions
List comprehensions refers to special syntactic constructs in some languages used for generating lists. F# has an expressive list comprehension syntax, which comes in two forms, ranges and generators.

Ranges have the constructs  and. For example:

Generators have the construct, and they are much more flexible than ranges. For example:

it's possible to loop over any collection, not just numbers. This example loops over a :

Note that the  keyword pushes a single value into a list. Another keyword,, pushes a collection of values into the list. The  keyword is used as follows:

It's possible to mix the  and   keywords:

Alternative List Comprehension Syntax
The samples above use the  keyword explicitly, however F# provides a slightly different arrow-based syntax for list comprehensions:

is equivalent to the  operator respectively. While it's still common to see list comprehensions expressed using, this construct will not be emphasized in this book since it has been deprecated in favor of.

Pattern Matching Lists
You use the same syntax to match against lists that you use to create lists. Here's a simple program: The  method has the type. It recursively enumerates through the list, adding each item in the list to the value. Step by step, the function works as follows:

Reversing Lists
Frequently, we use recursion and pattern matching to generate new lists from existing lists. A simple example is reversing a list:


 * Note to beginners: the pattern seen above is very common. Often, when we iterate through lists, we want to build up a new list. To do this recursively, we use an accumulating parameter (which is called  above) which holds our new list as we generate it. It's also very common to use a nested function, usually named   or , to hide the implementation details of the function from clients (in other words, clients should not have to pass in their own accumulating parameter).

has the type. You'd use this function as follows:

This simple function works because items are always prepended to the accumulating parameter, resulting in series of recursive calls as follows:

is a built-in function for reversing a list:

Filtering Lists
Oftentimes, we want to filter a list for certain values. We can write a filter function as follows:

The  method has the type. The program above outputs: filteredNumbers: [2; 4; 6; 8; 10]

We can make the  above tail-recursive with a slight modification:


 * Note: Since accumulating parameters often build up lists in reverse order, it's very common to see  called immediately before returning a list from a function to put it in correct order.

Mapping Lists
We can write a function which maps a list to another list:

has the type. The program above outputs: mappedNumbers: ["1"; "4"; "9"; "16"; "25"; "36"; "49"; "64"; "81"; "100"]

A tail-recursive map function can be written as:

Like the example above, we use the accumulating-param-and-reverse pattern to make the function tail recursive.

Using the Module
Although a reverse, filter, and map method were implemented above, it's much more convenient to use F#'s built-in functions:

  reverses a list:

  filters a list:

  maps a list from one type to another:

and the Operator
has the type:

As you can imagine, the  functions appends one list to another. The  operator is an infix operator which performs the same function:

Since lists are immutable, appending two lists together requires copying all of the elements of the lists to create a brand new list. However, since lists are immutable, it's only necessary to copy the elements of the first list; the second list does not need to be copied. Represented in memory, appending two lists can be diagrammed as follows:

We start with the following:

first = 1 :: 2 :: 3 :: []

second = 4 :: 5 :: 6 :: []

Appending the two lists,, results in the following: first = 1 :: 2 :: 3 :: [] \______ ______/                     \/        combined = 1 :: 2 :: 3 :: second (copied)

In other words, F# prepends a copy of  to   to create the   list. This hypothesis can be verified using the following in fsi: The two lists  and   are literally the same object in memory, meaning F# reused the nodes from   when constructing the new list.

Appending two lists,  and   has a space and time complexity of O(list1.Length).

has the following definition:

The  method is clever because it filters and maps a list simultaneously:

filters for items that return  and maps them to another value in a single step.

and
and  have the following definitions:

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 'State type represents the accumulated value, it is the output of one round of the calculation and input for the next round. This description makes fold operations sound more complicated, but the implementation is actually very simple:

applies a function to each element in the list from left to right, while  applies a function to each element from right to left. Let's examine the fold functions in more technical detail using the following example:

The value of  is. This table demonstrates how  was calculated:

passes an accumulator with an item from the list into a function. The output of the function is passed as the accumulator for the next item.

As shown above, the  function processes the list from the first item to the last item in the list, or left to right. As you can imagine,  works the same way, but it operates on lists from right to left. Given a fold function  and a list , the fold methods transform our lists in the following ways:

There are several other functions in the  module related to folding:
 * and : folds two lists together simultaneously.
 * and : same as   and , except it uses the first (or last) element in the list as the seed value.
 * and : similar to   and , except it returns all of the intermediate values as a list rather than the final accumulated value.

Fold functions can be surprisingly useful:

Summing the numbers 1 - 100

In F#, mathematical operators are no different from functions. As shown above, we can actually pass the addition operator to the  function, because the   operator has the definition.

Computing a factorial

Computing population standard deviation

and
and  have the following types:

The  and   methods return the first item in the list for which the search function returns. They only differ as follows: if no items are found that meet the search function,  throws a , while   returns.

The two functions are used as follows:

Exercises
Solutions.

Pair and Unpair
Write two functions with the following definitions:

The  function should convert a list into a list of pairs as follows:

The  function should convert a list of pairs back into a traditional list as follows:

Expand a List
Write a function with the following type definition:

The  function should expand a list as follows:

Greatest common divisor on lists
The task is to calculate the greatest common divisor of a list of integers.

The first step is to write a function which should have the following type: The  function should take two integers and return their greatest common divisor. Hint: Use Euclidean algorithm

The second step is to use the gcd function to calculate the greatest common divisor of an int list. The  function should work like this: If the list is empty the result shall be 0.

Basic Mergesort Algorithm
The goal of this exercise is to implement the mergesort algorithm to sort a list in F#. When I talk about sorting, it means sorting in an ascending order. If you're not familiar with the mergesort algorithm, it works like this:
 * Split: Divide the list in two equally large parts
 * Sort: Sort those parts
 * Merge: Sort while merging (hence the name)

Note that the algorithm works recursively. It will first split the list. The next step is to sort both parts. To do that, they will be split again and so on. This will basically continue until the original list is scrambled up completely. Then recursion will do it's magic and assemble the list from the bottom. This might seem confusing at first, but you will learn a lot about how recursion works, when there's more than one function involved

The  function

The  function will work like this. The split's will be returned as a tuple. The  function doesn't need to sort the lists though.

The  function

The next step is merging. We now want to merge the split's together into a sorted list assuming that both split's themselves are already sorted. The  function will take a tuple of two already sorted lists and recursively create a sorted list: Example: It is important to notice that the  function will only work if both split's are already sorted. It will make it's implementation a lot easier. Assuming both split's are sorted, we can just look at the first element of both split's and only compare which one of them is smaller. To ensure this is the case we will write one last function.

The  function

You can think of it as the function organising the correct execution of the algorithm. It uses the previously implemented functions, so it's able to take a random list and return the sorted list. How to implement msort:

If the list is empty or if the list has only one element, we don't need to do anything to it and can immediately return it, because we don't need to sort it.

If that's not the case, we need to apply our algorithm to it. First  the list into a tuple of two, then   the tuple while recursively sorting both arguments of the tuple.