Exercice 1 : Manipulations de listes (4 points)
Implémenter les 4 fonctions suivantes. Vous pouvez utiliser les fonctions de la librairie standard si vous le voulez.
val choose : int -> 'elt list -> 'elt option
let res = choose 2 ['a';'b';'c';'d']
val res : char option = Some 'c'
let res' = choose 4 ['a';'b';'c';'d']
val res' : char option = None
val at_least_42 : int list -> int list
let res = at_leat_42 [5;42;13;56;87;3]
val res : int list = [42;56;87]
val stutter : 'elt list -> 'elt list
let res = stutter [1;2;3;4]
val res : int list = [1;1;2;2;3;3;4;4]
val stutter_4 : 'elt list -> 'elt list
let res = stutter [1;2;3]
val res : int list = [1;1;1;1;2;2;2;2;3;3;3;3]
Solution :
let rec choose n = function
| head::_ when n = 0 -> Some head
| _::tail -> choose (n-1) tail
| [] -> Done
let at_least_42 = List.filter (fun i -> i >= 42)
let stutter list = List.map (fun elt -> [elt;elt]) list |> List.flatten
let stutter_4 list = list |> stutter |> stutter
Exercice 2 : Substitutions (6 points)
Évaluer par substitutions successives les 4 expressions suivantes.
let rec odd = fun list -> match list with
| head::tail -> head :: even tail
| [] -> []
and even = fun list -> match list with
| _::tail -> odd tail
| [] -> []
in odd [1;2;3;4;5;6]
let rec fib = fun i -> match i with
| 0 | 1 -> [0]
| _ -> (fib (i-1)) @ (fib (i-2))
in fib 3
type tree = Leaf | Node of (tree * int * tree)
let rec path = fun elt acc tree -> match tree with
| Node (_,root,_) when elt = root -> Some (List.rev acc)
| Node (left,root,_) when elt < root -> path elt (root::acc) left
| Node (_,root,right) -> path elt (root::acc) right
| Leaf -> None
in
let init_tree =
Node (
Node( Node(Node(Leaf,2,Leaf),3,Node(Leaf,4,Leaf)),
8,
Node(Node(Leaf,9,Leaf),11,Leaf)
),
12,
Node(Node(Leaf,13,Leaf), 15, Node(Leaf,18,Leaf))
)
in path 9 [] init_tree
let stack = fun one two (list1,list2) -> (one::list1,two::list2) in
let rec split = fun list -> match list with
| one::two::tail -> stack one two (split tail)
| [one] -> ([one],[])
| [] -> ([],[])
in split [0;1;2;3;4;5;6]
Solution :
odd [0;1;2;3;4;5;6]
--> 0 :: even [1;2;3;4;5;6]
--> 0 :: odd [2;3;4;5;6]
--> 0 :: 2 :: even [3;4;5;6]
--> 0 :: 2 :: odd [4;5;6]
--> 0 :: 2 :: 4 :: even [5;6]
--> 0 :: 2 :: 4 :: odd [6]
--> 0 :: 2 :: 4 :: 6 :: even []
--> [0; 2; 4; 6]
fib 4
--> fib 3 @ fib 2
--> fib 2 @ fib 1 @ fib 2
--> fib 1 @ fib 0 @ fib 1 @ fib 2
--> fib 1 @ fib 0 @ fib 1 @ fib 1 @ fib 0
--> [0] @ fib 0 @ fib 1 @ fib 1 @ fib 0
--> [0] @ [0] @ fib 1 @ fib 1 @ fib 0
--> ...
--> [0] @ [0] @ [0] @ [0] @ [0]
--> [0;0;0;0;0]
path 9 [] init_tree
--> path 9 [12]
Node( Node(Node(Leaf,2,Leaf),3,Node(Leaf,4,Leaf)),
8,
Node(Node(Leaf,9,Leaf),11,Leaf)
)
--> path 9 [8;12] Node(Node(Leaf,9,Leaf),11,Leaf)
--> path 9 [11;8;12] Node(Leaf,9,Leaf)
--> List.rev [11;8;12]
--> [12;8;11]
split [0;1;2;3;4;5;6]
--> stack 0 1 (split [2;3;4;5;6])
--> stack 0 1 (stack 2 3 (split [4;5;6])
--> stack 0 1 (stack 2 3 (stack 4 5 (split [6])))
--> stack 0 1 (stack 2 3 (stack 4 5 ([6],[]))
--> stack 0 1 (stack 2 3 ([4;6],[5]))
--> stack 0 1 ([2;4;6],[3;5])
--> ([0;2;4;6],[1;3;5])
Exercice 4 : Tirage au sort de la Coupe du Monde (6 points)
La Coupe du Monde de football se déroule actuellement au Brésil. Nous allons coder l'algorithme qui a permis de tirer au sort les 8 poules de 4 équipes qui constituent la première partie de la compétition.
Les 32 équipes sont classés en quatre chapeaux (pots en anglais). Ces chapeaux correspondent pour le premier aux meilleures équipes du classement international, et pour les trois autres aux origines géographiques :
type team = string
type pot = team list
let () = Random.self_init ()
let pot1 = ["Brasil"; "Uruguay"; "Columbia"; "Argentina"; "Switzerland"; "Germany"; "Spain"; "Belgium"]
let pot2 = ["Ghana"; "Ivory Coast"; "Algeria"; "Cameroon"; "Nigeria "; "Ecuador"; "Chile"]
let pot3 = ["Iran"; "Mexico"; "United States"; "Costa Rica"; "Australia"; "Japan"; "South Korea"; "Honduras"]
let pot4 = ["Netherlands"; "France"; "England"; "Portugal"; "Italy"; "Bosnia"; "Croatia"; "Greece"; "Russia"]
let init_pots = [pot1; pot2; pot3; pot4]
On note que la taille des chapeaux est 8,7,8,9 respectivement. L'idée générale de l'algorithme est de constituer chaque poule (pool en anglais) en tirant une équipe dans chaque chapeau. Cela nécessite que chaque chapeau soit de taille 8 , donc dans une première étape, une équipe du chapeau 4 sera reversé dans le chapeau 2 .
Question 1 : Extraire un élément choisi aléatoirement dans une liste
On définit la fonction suivante pour choisir un élément aléatoire dans une liste.
let choose_and_remove list =
let index = Random.int (List.length list) in
let rec remove i ignored list =
match list with
| head::tail when i = 0 -> (head,List.rev_append ignored tail)
| head::tail -> remove (i-1) (head::ignored) tail
| [] -> assert false
in remove index [] list
Évaluer par substitutions successives l'expression :
choose_and_remove [2;1;4;6;3;5]
en supposant que l'appel Random.int 6 renvoie 3 . (Pour rappel, Random.int n s'évalue en un entier compris entre $0$ et $n-1$ inclus, choisi aléatoirement.)
Quel est le type de choose_and_remove ? (ne pas justifier la réponse)
Solution :
choose_and_remove [2;1;4;6;3;5]
--> remove 3 [] [2;1;4;6;3;5]
--> remove 2 [2] [1;4;6;3;5]
--> remove 1 [1;2] [4;6;3;5]
--> remove 0 [4;1;2] [6;3;5]
--> (6, List.rev_append [4;1;2] [3;5])
--> (6, [2;1;4;3;5])
Le type de choose_and_remove est 'elt list -> 'elt * 'elt list.
Question 2 : Appliquer une fonction à un élément.
Nous souhaitons modifier une liste en appliquant une fonction à son
$i$e élément, les autres éléments restant inchangés. Écrire la fonction suivante pour se faire :
val apply_at_nth : ('elt -> 'elt) -> int -> 'elt list -> 'elt list
let res = apply_at_nth (fun x -> 3 * x) 2 [1;2;3;4;5]
val res : int list = [1; 2; 9; 4; 5]
let res' = apply_at_nth (fun x -> 3 * x) 4 [1;2;3;4;5]
val res' : int list = [1; 2; 3; 4; 15]
S'il n'y a pas de $i$e élément, la liste evaluée est identique à celle passée en argument.
En déduire une fonction permettant de remplacer le $i$e élément par un autre :
val replace_nth : int -> 'elt -> 'elt list -> 'elt list
let res = replace_nth 2 42 [1;2;3;4;5];;
val res : int list = [1; 2; 42; 4; 5]
let res' = replace_nth 4 42 [1;2;3;4;5];;
val res' : int list = [1; 2; 3; 4; 42]
Solution :
let rec apply_at_nth f n list =
match list with
| head::tail when n = 0 -> f head :: tail
| head::tail -> head :: apply_at_nth f (n-1) tail
| [] -> []
let replace_nth n elt list = apply_at_nth (fun _ -> elt) n list
Question 3 : Reverser du chapeau $i$ vers le chapeau $j$
Écrire une fonction prenant une liste de chapeau (type pot), et qui retire une équipe choisit aléatoirement dans le $i$e chapeau, et l'ajoute au $j$e chapeau.
val initial_move : int -> int -> pot list -> pot list
let after_balance = initial_move 4 2 init_pots;;
val after_balance : pot list =
[["Brasil"; "Uruguay"; "Columbia"; "Argentina"; "Switzerland"; "Germany"; "Spain"; "Belgium"];
["Russia"; "Ghana"; "Ivory Coast"; "Algeria"; "Cameroon"; "Nigeria "; "Ecuador"; "Chile"];
["Iran"; "Mexico"; "United States"; "Costa Rica"; "Australia"; "Japan"; "South Korea"; "Honduras"];
["Netherlands"; "France"; "England"; "Portugal"; "Italy"; "Bosnia"; "Croatia"; "Greece"]
]
Vous pouvez utiliser la fonction List.nth : 'elt list -> int -> 'elt pour récupérer un élément en position $i$ dans la liste (attention : le premier élément est en position 0).
Solution :
let initial_move exc def init_pots =
let (team_exc,pot_exc) = choose_and_remove (List.nth init_pots (exc-1)) in
init_pots
|> apply_at_nth (fun pot_def -> team_exc::pot_def) (def-1)
|> replace_nth (exc-1) pot_exc
Question 4 : Tirer une poule
Écrire une fonction make_a_pool : pot list -> team list * pot
list, qui choisit aléatoirement une équipe de chaque chapeau, et retourne la poule ainsi formée, ainsi que la liste des chapeaux sans ces équipes. Vous pouvez supposer que chaque chapeau est non-vide, mais la liste des chapeaux peut être de n'importe quelle longueur.
let pool_example = make_a_pool after_balance
val pool_example : string list * string list list =
( ["Belgium"; "Cameroon"; "Japan"; "France"],
[ ["Brasil"; "Uruguay"; "Columbia"; "Argentina"; "Switzerland"; "Germany"; "Spain"];
["Russia"; "Ghana"; "Ivory Coast"; "Algeria"; "Nigeria "; "Ecuador"; "Chile"];
["Iran"; "Mexico"; "United States"; "Costa Rica"; "Australia"; "South Korea"; "Honduras"];
["Netherlands"; "England"; "Portugal"; "Italy"; "Bosnia"; "Croatia"; "Greece"]])
Solution :
let make_a_pool state =
List.map choose_and_remove state
|> List.split
Question 5 : Tirer toutes les poules
En déduire une fonction draw_world_cup : unit -> team list list, retournant une liste de poules pour la Coupe du Monde, tirée selon les règles.
# let _ = draw_world_cup ();;
- : string list list =
[ ["Argentina"; "Cameroon"; "United States"; "Portugal"];
["Spain"; "Ivory Coast"; "Mexico"; "Bosnia"];
["Brasil"; "Ecuador"; "Japan"; "Netherlands"];
["Belgium"; "Algeria"; "South Korea"; "Greece"];
["Uruguay"; "Chile"; "Costa Rica"; "England"];
["Germany"; "Nigeria "; "Australia"; "Russia"];
["Columbia"; "Ghana"; "Honduras"; "Italy"];
["Switzerland"; "Croatia"; "Iran"; "France"]
]
Solution :
let rec make_all_pools pools pots =
if List.for_all (fun pot -> pot = []) pots then pools
else
let (one_pool,next_pots) = make_a_pool pots in
make_all_pools (one_pool::pools) next_pots
let draw_world_cup () = make_all_pools [] (initial_move 4 2 init_state)
Éléments de langage
Fonctions utiles de la librairie standard :
val max : 'a -> 'a -> 'a
val sqrt : float -> float
val ( ** ) : float -> float -> float
val (@) : 'a list -> 'a list -> 'a list
type 'elt option =
| Some of 'elt
| None
Fonctions utiles du module List :
val hd : 'elt list -> 'elt
val tl : 'elt list -> 'elt list
val length : 'a list -> int
val nth : 'a list -> int -> 'a
val rev : 'a list -> 'a list
val flatten : 'a list list -> 'a list
val map : ('a -> 'b) -> 'a list -> 'b list
val fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
val fold_right : ('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
val for_all : ('a -> bool) -> 'a list -> bool
val exists : ('a -> bool) -> 'a list -> bool
val mem : 'a -> 'a list -> bool
val filter : ('a -> bool) -> 'a list -> 'a list
val fast_sort : ('a -> 'a -> int) -> 'a list -> 'a list
Exemples de syntaxe :
type ratio = int * int
let rec euclid a b =
if b = 0 then a
else euclid b (a mod b)
let pgcd a b = euclid (abs a) (abs b)
let ratio p q =
let d = pgcd p q in
if q < 0 then (-p / d, q / d)
else (p/d, q/d)
let (+/) (a,b) (c,d) = ratio (a*d + b*c) (b*d)
let (++) (a,b) (c,d) = ratio (a+c) (b+d)
module Queue =
struct
type 'a t =
Queue of ('a list) * ('a list)
exception Empty
let empty = Queue ([],[])
let is_empty = function
| Queue ([],[]) -> true
| _ -> false
let queue = function
| Queue ([],l) -> Queue(List.rev l,[])
| x -> x
let snoc (Queue (l1,l2)) ele =
queue (Queue (l1,ele::l2))
let head = function
| Queue ([],_) -> raise Empty
| Queue (l,_) -> List.hd l
let tail = function
| Queue ([],_) -> raise Empty
| Queue (l1,l2) -> queue (Queue (List.tl l1,l2))
end