#+TITLE: 99 Ocaml Problems #+Author: Joseph Ferano https://ocaml.org/problems *** #1 Tail of a list Write a function ~last : 'a list -> 'a option~ that returns the last element of a list #+begin_src ocaml # last ["a" ; "b" ; "c" ; "d"];; - : string option = Some "d" # last [];; - : 'a option = None #+end_src **** Solution #+begin_src ocaml let rec last = function | [] -> None | [x] -> Some x | _::xs -> last xs #+end_src *** #2 Last two elements of a list Find the last but one (last and penultimate) elements of a list. #+begin_src ocaml # last_two ["a"; "b"; "c"; "d"];; - : (string * string) option = Some ("c", "d") # last_two ["a"];; - : (string * string) option = None #+end_src **** Solution #+begin_src ocaml let rec last_two = function | [] | [_] -> None | [x;y] -> Some (x,y) | _::xs -> last_two xs #+end_src *** #3 N'th element of a list Find the N'th element of a list. REMARK: OCaml has List.nth which numbers elements from 0 and raises an exception if the index is out of bounds. #+begin_src ocaml # List.nth ["a"; "b"; "c"; "d"; "e"] 2;; - : string = "c" # List.nth ["a"] 2;; Exception: Failure "nth". #+end_src **** Solution #+begin_src ocaml let rec nth index = function | [] -> failwith "nth" | x::xs -> if index = 0 then x else nth (index - 1) xs #+end_src *** #4 Find the number of elements of a list. OCaml standard library has List.length but we ask that you reimplement it. Bonus for a tail recursive solution. #+begin_src ocaml # length ["a"; "b"; "c"];; - : int = 3 # length [];; - : int = 0 #+end_src **** Solution #+begin_src ocaml let length list = let rec length_rec acc = function | [] -> acc | _::xs -> length_rec (acc + 1) xs in length_rec 0 list #+end_src *** #5 Reverse a list. OCaml standard library has List.rev but we ask that you reimplement it. #+begin_src ocaml # rev ["a"; "b"; "c"];; - : string list = ["c"; "b"; "a"] #+end_src **** Solution #+begin_src ocaml let rev (list: int list) = let rec rev acc = function | [] -> acc | x::xs -> rev (x::acc) xs in rev [] list #+end_src *** #6 Palindrome Find out whether a list is a palindrome. HINT: a palindrome is its own reverse. #+begin_src ocaml # is_palindrome ["x"; "a"; "m"; "a"; "x"];; - : bool = true # not (is_palindrome ["a"; "b"]);; - : bool = true #+end_src **** Solution #+begin_src ocaml let is_palindrome list = list = List.rev list #+end_src *** #7 Flatten a list Flatten a nested list structure. #+begin_src ocaml type 'a node = | One of 'a | Many of 'a node list # flatten [One "a"; Many [One "b"; Many [One "c" ;One "d"]; One "e"]];; - : string list = ["a"; "b"; "c"; "d"; "e"] #+end_src **** Solution #+begin_src ocaml let flatten nodes = let rec f = function | One x -> [x] | Many xs -> List.concat_map f xs in nodes |> List.concat_map f (* Or without List.concat_map *) let flatten2 nodes = let rec f acc = function | [] -> acc | One n::ns -> f (n::acc) ns | Many ns::rest -> f (f acc ns) rest in f [] nodes |> List.rev #+end_src *** #8 Eliminate duplicates Eliminate consecutive duplicates of list elements. #+begin_src ocaml # compress ["a"; "a"; "a"; "a"; "b"; "c"; "c"; "a"; "a"; "d"; "e"; "e"; "e"; "e"];; - : string list = ["a"; "b"; "c"; "a"; "d"; "e"] #+end_src **** Solution #+begin_src ocaml let compress list = let rec f acc list = match (list, acc) with | [] , _ -> acc | x::xs , y::_ when x = y -> f acc xs | x::xs , _ -> f (x::acc) xs in f [] list |> List.rev #+end_src *** #9 Pack consecutive duplicates Pack consecutive duplicates of list elements into sublists. #+begin_src ocaml # pack ["a"; "a"; "a"; "a"; "b"; "c"; "c"; "a"; "a"; "d"; "d"; "e"; "e"; "e"; "e"];; - : string list list = [["a"; "a"; "a"; "a"]; ["b"]; ["c"; "c"]; ["a"; "a"]; ["d"; "d"]; ["e"; "e"; "e"; "e"]] #+end_src **** Solution #+begin_src ocaml let rec pack list = let rec f acc1 acc2 = function | [] -> [] | [x] -> (x::acc1)::acc2 | x::(y::xs as tail) -> if x = y then f (x::acc1) acc2 tail else f [] ((x::acc1)::acc2) tail in f [] [] list |> List.rev #+end_src *** #10 Run-length encoding If you need so, refresh your memory about run-length encoding. Here is an example: #+begin_src ocaml # encode ["a"; "a"; "a"; "a"; "b"; "c"; "c"; "a"; "a"; "d"; "e"; "e"; "e"; "e"];; - : (int * string) list = [(4, "a"); (1, "b"); (2, "c"); (2, "a"); (1, "d"); (4, "e")] #+end_src **** Solution #+begin_src ocaml let encode = function | [] -> [] | list -> let rec f (count, item) acc2 = function | [x] -> (count, item)::acc2 | x::(y::xs as tail) when x = y -> f (count + 1, item) acc2 tail | x::(y::xs as tail) -> f (1, y) ((count, item)::acc2) tail | [] -> [] in f (1, List.nth list 0) [] list |> List.rev #+end_src *** #11 Modified run-length encoding Modify the result of the previous problem in such a way that if an element has no duplicates it is simply copied into the result list. Only elements with duplicates are transferred as (N E) lists. Since OCaml lists are homogeneous, one needs to define a type to hold both single elements and sub-lists. #+begin_src ocaml type 'a rle = | One of 'a | Many of int * 'a # encode ["a"; "a"; "a"; "a"; "b"; "c"; "c"; "a"; "a"; "d"; "e"; "e"; "e"; "e"];; - : string rle list = [Many (4, "a"); One "b"; Many (2, "c"); Many (2, "a"); One "d"; Many (4, "e")] #+end_src **** Solution #+begin_src ocaml let encode = function | [] -> [] | list -> let rec f (count, elem) acc2 = function | [] -> [] | [x] when count = 0 -> (One x)::acc2 | [x] -> (Many (count + 1, x))::acc2 | x::(y::xs as tail) when x = y -> f (count + 1, elem) acc2 tail | x::(y::xs as tail) when count = 0 -> f (0, y) ((One x)::acc2) tail | x::(y::xs as tail) -> f (0, y) ((Many (count + 1, x))::acc2) tail in f (0, List.nth list 0) [] list |> List.rev #+end_src *** #12 Decode a run-length encoded list Given a run-length code list generated as specified in the previous problem, construct its uncompressed version. #+begin_src ocaml # decode [Many (4, "a"); One "b"; Many (2, "c"); Many (2, "a"); One "d"; Many (4, "e")];; - : string list = ["a"; "a"; "a"; "a"; "b"; "c"; "c"; "a"; "a"; "d"; "e"; "e"; "e"; "e"] #+end_src **** Solution #+begin_src ocaml #+end_src