Ninety-Nine Problems in Ocaml

Exercise 1: Tail of a List

Write a function that returns the last element of a list.

last : 'a list -> 'a option 
Show Solution ```ocaml let rec last (l: 'a list): 'a option = match l with | [] -> None | [e] -> Some e | _::tail -> last tail ```

Explanation

Here we are defining a recursive function in OCaml.

Notice the optional type hints. The function takes a (homogenous) list l of a generic type 'a ('SOMETHING is OCaml syntax for generic types).

If the list is empty, we return None, there is no last element.

If the list contains a single element e, return Some e.

Otherwise, discard the first element _ and call last on the rest of the list.

'a option is OCaml's solution to the billion dollar mistake of the NullPointerException. All values in OCaml are non-null.

The option type allows us to deal with a case where a "null" value would be useful.

type 'a option = 
| Some 'a
| None

The compiler will force you to handle None values in option types.

The e::l operation appends element e to the beginning of the list l.

The match allows us to perform different action based on the form of the list l. It is basically an switch statement on steroids that allows us to deconstruct a value of any type into its possible forms.

The compiler will force you to handle all possible forms of the input type.

Exercise 2: Last Two Elements of a List

Find the last but one (last and penultimate) elements of a list.

last_two : 'a list -> ('a * 'a) option 
Show Solution
 let rec last_two (l: 'a list): ('a * 'a) option = 
   match l with
   | [] 
   | [_] -> None
   | e1::e2::[] -> Some (e1, e2)
   | _::rest -> last_two rest

Explanation

Same idea, minor tweaks. We are returing a product type ie: tuple.

If the list contains exactly two elements e1 and e2, return the tuple (e1, e2) wrapped in Some (we HAVE to return an option type).

If the list contains 0 or 1 elements, return None, we can match both cases with one arrow.

Otherwise if the list contains 3 or more elements, discard the first one and call last_two on the rest of the list.

Exercise 3: N'th Element of a List

Find the N'th element of a list.

nth: 'a list -> int -> 'a option
Show solution
let rec nth (lst: 'a list) (k: int): 'a option = 
  match (k, lst) with 
  | (_, []) -> None
  | (0, x::_) -> Some x
  | (k, _::rest) -> nth rest (k-1)

This function still returns None for negative values.

Exercise 4: Length of a List

Find the number of elements of a list.

length: 'a list -> int
Show solution
let rec length (lst: 'a list): int = 
  match lst with
  | [] -> 0
  | _::rest -> 1 + length rest

Explanation

The length of an empty list is 0.

The length of a list having a first element is one plus the length of the rest of the list.

Here is a tail recursive version.

let rec length (lst: 'a list): int = 
  (* Define an inner auxiliary function *)
  let rec aux (lst: 'a list) (sofar: int): int = 
    match lst with
    | [] -> sofar
    | _::rest -> aux rest (sofar + 1)

  (* return this expression that uses the auxiliary function *)
  in aux lst 0

Exercise 5: Reverse a List

Reverse a List

rev: 'a list -> 'a list
Show solution
let rec rev (lst: 'a list): 'a list =
  let rec aux (reversed: 'a list) (remaining: 'a list): 'a list = 
    match (reversed, remaining) with
    | (reversed, []) -> reversed
    | (reversed, x::tail) -> aux (x::reversed) tail
  in aux [] lst

Exercise 6: Duplicate the Elements of a List

Duplicate the Elements of a List.

dupl: 'a list -> 'a list
Show solution ```ocaml let rec dupl (lst: 'a list): 'a list = match lst with | [] -> [] | x::rest -> x::x::(dupl rest) ```

Exercise 7: Split a List Into Two Parts

Split a list into two parts; the length of the first part is given.

If the length of the first part is longer than the entire list, then the first part is the list and the second part is empty.

split: 'a list -> int -> 'a list * 'a list 
Show solution ```ocaml let split (l: 'a list) (n: int): 'a list * 'a list = let rec aux (f: 'a list) (l: 'a list) (r: int) : 'a list * 'a list = match (r, l) with | (0, l) | (k, []) -> (List.rev f, l) | (k, x::rest) -> aux (x::f) (rest) (k-1) in aux [] l n ```

Exercise n: ptitle

pdescription

psig
Show solution ```ocaml (* solution *) ```

Explanation

pexpl