As a warm-up, some classical exercises on lists (yes, this is quite long).
Exercise : First-order functions on lists
- Write a function nth with type α list → int → α such that
nth alist n
returns the n-th element of alist (counting from 0), or fails with failwith "List too small"
if n is bigger than the list. - Write a function rev with type α list → α list which reverses the elements of the list:
rev [ 1 ; 2 ; 3 ]
is [ 3 ; 2 ; 1 ]
.
You need an auxiliary function with an accumulator (this is clearly the easiest way to reverse a list). If you work well, the auxiliary function should be hidden from the outside world. - Write a function append with type α list → α list → α list which concatenates two lists. No accumulator, this function does not exist in tail-recursive form (unless you make two passes). For example,
append [ 1 ; 2 ] [ 3 ; 4 ]
is [ 1 ; 2 ; 3 ; 4]
. - Write the tail-recursive variant: rev_append with the same type, but a different behaviour.
rev_append l1 l2
returns a list: (reversed-l1) then (l2). For instance, rev_append [ 1 ; 2 ] [ 3 ; 4 ]
is [ 2 ; 1 ; 3 ; 4]
. (You can write an auxiliary function if necessary).
Exercise : Find your way in the OCaml manual
- Search for
ocaml manual
using whatever search engine. - You should find a page entitled
The OCaml system release 4.04
(the precise version number does not matter) . - Identify:
- Which part explains the compiler options, the runtime options, the documentation generator, etc. Find the list of compiler options. Do not read them all!
- Which part contains a few tutorials. Find the tutorial about objects. You'll read it this summer.
- Which part contains the formal definition of the language: you should find the definition of expressions.
- Which part contains the libraries. Find the standard library, in which you find the List module.
- In the documentation of the List module, read the first paragraph and the documentation of the first block of functions (until Iterators). Almost all functions should sound familiar.
- Finally, have a quick look at the definitions in the Pervasive module, to be found in the Core Library. All these definitions are always available by default in OCaml. Read the boolean operations.

Exercise : Higher-order functions on lists
- Remember the function map you have written in Lesson 2 (higher-order functions).
Its type is ∀αβ. (α → β) → α list → β list. It is not tail-recursive, it does not use an accumulator. - Write the tail-recursive version rev_map with the same type. The function takes as an argument a list
[ a ; b ; c ; ...]
and returns a reversed list: [ ... ; f c ; f b ; f a ]
You need an auxiliary function, which can be carefully hidden from the outside world. - Write a function iter with type ∀α. (α → unit) → α list → unit such that
iter f [ a ; b ; c ; ... ]
is equivalent to f a ; f b ; f c ; ...
(it is a sequence). The type checker will probably infer a more polymorphic type: ∀α. (α → β) → α list → unit, that's ok.
As an example, iter (Printf.printf "Element : %d\n%!") [ 2 ; 4 ; 6 ; 8 ]
should print the elements of the list. - This function is not in the standard library: write print_list with type ∀α. string → (α → string) → α list → unit such that
print_list sep conv alist
prints the elements of the list, using conv
to convert them to strings, and using sep
as a separator (for example, a comma).print_list ", " string_of_int [ 4 ; 8 ; 99 ]
| 4, 8, 99 |
print_list " ++ " (fun x -> x) [ "aa" ; "bb" ; "cc" ]
| aa ++ bb ++ cc |
- Note: to be fully compositional, print_list should return a string rather than use Printf. (Exercise to be done on your own, later).
- Finally, we get in touch with the essence of iterators on lists: fold
We wish to write a function fold able to add all elements of a list, or to multiply all elements, or apply any operation.fold (+) 0 [ 1 ; 2 ; 3 ; 4 ]
| 10 | 0 is the accumulator. |
fold ( * ) 1 [ 1 ; 2 ; 3 ; 4 ]
| 24 | Put the spaces around ( * ) otherwise it is considered as a comment. |
Complete this definition (keep the type annotations):
let rec fold (op:int -> int -> int) (acu:int) = function
| ...
Test with the examples above. Make sure that the operator op
is applied to the acu first, then to an element of the list (otherwise your type variables will be inverted in the next question). - Remove the type annotations on fold. You shoud get a polymorphic type: (α → β → α) → α → β list → α.
(Again, you have written a polymorphic function without noticing.)
Take the necessary time to understand this type, as well as the following examples: fold (fun a b -> a ^ " " ^ string_of_int b) "" [ 1 ; 2 ; 3 ; 4]
| Guess what is the result. |
fold (fun a b -> if a < b then b else a) 0 [ 10 ; 40 ; 20 ; 30 ]
| Guess. |
fold (fun a b -> b :: a) [] [ 4 ; 3 ; 2 ; 1 ]
| Guess. |
- In the List module, read the section about Iterators. fold is named fold_left. You use it with
List.fold_left
- Write a function exists with type (α → bool) → α list → bool such that
exists pred alist
returns true iff the predicate pred is true for at least one element of alist.
Write two versions: one using fold and one writing the recursive function directly, with pattern-matching.
exists (fun x -> x < 10) [ 20 ; 5 ; 30 ]
|
exists (fun x -> x < 10) [ 20 ; 40 ; 30 ]
|
exists (fun x -> x < 10) []
|
- Define a composition operator ++ (to define this infix operator:
let (++) f g ...
) which composes two functions (the usual composition written ∘ in maths).
((fun x -> x - 10) ++ abs) (-20)
| 10 |
(abs ++ (fun x -> x - 10)) (-20)
| 30 |
- Write the function forall, which has type (α → bool) → α list → bool which returns true iff the predicate is true for all elements of the list. Do not use fold, do not use pattern-matching, use only exists and your wits. (You may use ++ too.)
Exercise : Association lists
A classical use of lists consists in association lists which are lists of pairs. Here is an example, indicating which people can speak japanese: let assoc1 = [ ("Lucy", true) ; ("Mike", false) ; ("Hilary", false) ; ("Donald", true) ]
- Write a function assoc with type α → (α * β) list → β such that
assoc key assoc_list
returns the value associated to key in assoc_list. It fails with raise Not_found
if the key is not in the list (the exception Not_found is already defined). assoc "Donald" assoc1
| true |
assoc "Mike" assoc1
| false |
assoc "donald" assoc1
| Not_found |
- Write a function remove_assoc with type α → (α * β) list → (α * β) list. It removes a key from an association list. It fails with Not_found if the key is not in the list.
Test it.