CORRIGÉ INF121 : CONTRÔLE FINAL (3h00) Mai 2012 DOCUMENTS AUTORISÉS : Uniquement une feuille A4 recto-verso de notes personnelles manuscrites. Notes : Vous répondrez aux exercices et au problème dans l ordre qui vous convient. Le total des points de chaque question correspond au nombres de minutes nécessaires pour résoudre la question.vous pouvez admettre le résultat d une question, et l utiliser dans la suite. Exercice 1 : Typage et valeur d une expression (35 points = 5+5+5+10+10) Pour chaque programme OCaml, indiquez les types OCaml (polymorphe si possible) des fonctions et ce qu elles font ; pour vous aider vous donnerez aussi dans votre réponse des exemples d execution. Toutes les fonctions son bien typées. 1. let rec f1 b = match b with c::d -> 1 + (f1 d);; f1 calcule le nombre d élément de la liste b. val f1 : α list int = <fun> 2. let rec f2 a r = match a with [] -> r c::d -> f2 d (1 + r);; f1 calcule le nombre d élément de la liste b plus r. val f2 : α list int int = <fun> 3. let rec f3 a r = match a with c::d -> f3 d (1 + r);; f3 renvoie toujours la valeur 0. val f3 : α list int int = <fun> 4. let rec f4 p q = match (p, q) with Exercice 2 : Modélisation (15 points) ([],[]) -> true (x::y,[]) -> false ([],z::t) -> true (u::v,w::t)-> if u=w then f4 v t else f4 (u::v) t;; f4 détermine si la liste p est un sous sequence de la liste q. val f4: α list α list bool =<fun> 5. let rec f5 h i j = match i with [] -> j x::y -> h x (f5 h y j);; f5 est le code de la fonction fold_right, qui accumule les résultats de l application successive de la fonction h sur la liste i avec comme premier élément j. val f5 : (α β β) α list β β = <fun> Nous souhaitons modéliser un jeu pour enfant permettant d apprendre l addition et la multiplication des fractions. Ce jeu comporte des cartes représentant deux sortes de cartes : Les cartes représentant des chiffres de 0 à 9, Les cartes représentant des symboles +,,, = et / la barre des fractions. Chaque joueur commence le jeu avec un certain nombre de cartes. Chaque joueur placera des cartes sur le jeu et en obtiendra d autres au cours de la partie. 1. (5 points) Définir les types permettant de modéliser ce jeu. type carte = Chiffre of int Plus Moins Fois Divise Egal;; type main = carte list;; 2. (5 points) Donner un code OCaml d une fonction qui calcule le nombre de chiffres possédés par un joueur. let rec nbchiffre (l:carte list) : int = match l with x::r -> let nbc = nbchiffre r in ( match x with Chiffre(_) -> 1 + nbc _ -> nbc );; 3. (5 points) Donner un code OCaml d une fonction qui calcule le nombre de symboles possédés par un joueur. 1
let rec nbsymbole (l:carte list) : int = match l with x::r -> let nbs = nbsymbole r in ( match x with Chiffre(_) -> nbs _ -> 1 + nbs );; Exercice 3 : Deux occurrences exactement (70 points) Pour résoudre une question vous pouvez utiliser les fonctions demandées dans les questions précédentes, mais il est interdit d utiliser les fonctions de la bibliothèque List si cela n est pas explicitement demandé. 1. (10 points) Définir une fonction récursive polymorphe appart : α α list bool qui détermine si un élément appartient à une liste. Donner explicitement : Spécification : (a) Profil (b) Exemples Spécification : Réalisation : (a) Équations récursives (b) Terminaison (c) Code OCaml (a) Profil : val appartient : α α list bool = <fun> (b) Exemples : appartient 1 [2;1;3] = true appartient 1 [] = false Réalisation : (a) Équations récursives appartient x [] = false appartient x n : :r = (n=x) (appartient x r) (b) Terminaison : l appel récursif se fait sur une liste de taille plus petite donc la fonction de mesure longueur d une liste décroit donc la fonction termine. (c) Code OCaml let rec appartient (elt : e) (seq: e list) : bool = match seq with [] -> false h::t when h=elt -> true h::t -> appartient elt t;; ALTERNATIVE let rec member (l: a list) (e: a):bool = match l with [] -> false; x::r -> (x = e) (member r e);; 2. (6 points) Donner des exemples et un code OCaml d une fonction récursive polymorphe supprime : α α list α list qui supprime la première occurrence d un élément dans une liste. let rec supprimefirst (l: a list) (e: a): a list = match l with [] -> []; x::r -> if (x = e) then r else x::(supprimefirst r e);; let l1=[1;2;3;4;5;6;2;3;2;3;4];; supprimefirst l1 4;; (* [1;2;3;4;5;6;2;3;2;3] *) supprimefirst l1 6;; (* [1;2;3;4;5;2;3;2;3;4] *) 2
3. (6 points) Donner des exemples et un code OCaml d une fonction récursive polymorphe supprimetous : α α list α list qui supprime toutes les occurrences d un élément dans une liste. let rec supprimeall (l: a list) (e: a): a list = match l with [] -> []; x::r -> if (x = e) then supprimeall r e else x::(supprimeall r e);; let l2=[1;2;3;4;5;6;2;3;2;3];; supprimeall l1 4;; (* [1;2;3;4;5;6;2;3;2;3] *) supprimeall l2 2;; (* [1;3;4;5;6;3;3] *) 4. (12 points) Donner des exemples et un code OCaml d une fonction polymorphe récursive ou non deuxocc : α list bool qui détermine si une liste d éléments contient exactement deux occurrences d un élément. let rec deuxocc (l: a list):bool = match l with [] -> false [x] -> false x::r -> ((member r x)&& (not (member (supprimefirst r x) x ))) deuxocc (supprimeall r x);; deuxocc l1 ;; (* true *) deuxocc l2 ;; (* false *) let deuxocc (elt: e) (seq: e list) : bool = let moins1 = otepremiereocc elt seq in let moins2 = otepremiereocc elt moins1 in let moins3 = otepremiereocc elt moins2 in (seq <> moins1) && (moins1 <> moins2) && (moins2 == moins3) (* ou (not (appartient elt moins2)*) ;; 5. (12 points) Donner des exemples et un code OCaml d une fonction récursive polymorphe liste2occurrences : α list α list qui calcule la liste des éléments contenus chacun exactement 2 fois dans une liste. let rec ldeuxocc (l: a list): a list = match l with [] -> [] [x] -> [] x::r -> if (member r x)&& (not (member (supprimefirst r x) x )) then x::(ldeuxocc (supprimeall r x)) else ldeuxocc (supprimeall r x) ;; ldeuxocc l2;; (* [] *) ldeuxocc l1;; (* [4] *) Pour les deux questions suivantes, on utilisera les fonctions : map : (α β) α list β list List.map f [a1;...; an] applique la fonction f à a1,..., an, et construit la liste [f a1;...; f an]. fold_left : (α β α) α β list α List.fold_left f a [b1;...; bn] est f (... (f (f a b1) b2)...) bn. List.filter : (α bool) α list α list List.filter p l retourne tous les éléments de la liste l qui satisfont le predicat p. 6. (12 points) Donner un code OCaml d une fonction polymorphe appartbis : α list bool qui détermine si un élément appartient à une liste (question 1) en utilisant les fonctions d ordre supérieur proposées. let appartient elt seq = fold_left (or) (map (fun x -> x=elt) seq);; let appartient let seq = (filter (fun x -> x=elt) seq)!= [];; 3
7. (12 points) Donner un code OCaml d une fonction polymorphe supprimetousbis : α list α list qui supprime toutes les occurrences d un élément dans une liste (question 3) en utilisant les fonctions d ordre supérieur proposées. let oteoccurences elt seq = filter (fun x -> x!= elt) seq;; Exercise 4 : Arbres et multi-ensembles (60 points) Lors du projet vous deviez implementer le type abstrait de données multi-ensemble. Pour ce faire nous avons introduit le type e multielt pour représenter plusieurs exemplaires d une même valeur d élément : type e multielt = e int. Ainsi un multi-ensemble est une collection d éléments non ordonnés avec répétitions. Nous l avions représenté par une liste de multi-éléments, où tous les exemplaires d une valeur d élément sont regroupés dans un seul multi-élément pour chaque élèment. Afin de rendre plus efficace cette structure de données nous allons représenter un multi-ensemble par un arbre binaire de recherche de multi-éléments. type e multiens = Vide Multiset of e multiens e multielt e multiens Donner des exemples et un code OCaml des fonctions suivantes : 1. (5 points) estvidemultiens: e multiens bool est vrai si le multi-ensemble est vide. let estvidemultiens (ms: e multiens): bool = (ms =Vide);; estvidemultiens Vide;; (* true *) estvidemultiens Multiset(Vide,(1,2),Vide);; (* false *) 2. (5 points) cardinalmultiens: e multiens int retourne le nombre total des occurrences des e éléments présents dans le multi-sensemble. Donner les équations récursives et une preuve de terminaison de cette fonction. Equations récursives : cardinalmultiens Vide = 0 cardinalmultiens Multiset(g,(e,card),d) = cardinalmultiens g + cardinalmultiens d + card ou Terminaison : Les appels récursifs portent sur des arbres de tailles plus petits (g et d), la fonction décroit donc. Ainsi cardinalmultiens termine. Exemples : cardinalmultiens Vide;; (*(0,0)*) cardinalmultiens (Vide,(1,2), cardinalmultiens (Vide,(2,2),Vide));; (*(3,4)*) Code : let rec cardinalmultiens (ms: e multiens) : int*int = match ms with Vide -> (0,0) Multiset(g,(e,card),d) -> let (nbeltg,nbtotg) = cardinalmultiens g in let (nbeltd,nbtotd) = cardinalmultiens d in (nbeltg+nbeltd+1,nbtotg+nbtotd+card);; Dans un arbre binaire de recherche nous imposons que pour tout noeud s d un multi-ensemble, les contenus des noeuds du sous-arbre gauche de s sont strictement inférieurs au contenu de s, et que les contenus des noeuds du sous-arbre droit de s sont supérieurs au contenu de s. Pour cela, nous définissons un ordre sur les éléments du type e multielt : si (e 1, n 1 ) et (e 2, n 2 ) sont de type e multielt, (e 1, n 1 ) (e 2, n 2 ) si et seulement si e 1 e 2. Donner un code OCaml des fonctions suivantes en tenant compte de la propriété arbre binaire de recherche de l arbre représentant un multi-ensemble. 3. (5 points) Donner une représentation des multi-ensembles suivants : {1, 2, 1}, {1, 1, 2, 3, 3, 4}. Multiset(Vide,(1,2),Multiset(Vide,(2,1),Vide));; Multiset(Vide,(1,2),Multiset(Multiset(Vide,(2,1),Vide)),(3,2), Multiset(Vide,(4,1),Vide)));; 4. (5 points) appartientmultiens: e e multiens bool teste l appartenance d un élément à un multi-ensemble. 4
let rec appartientmultiens (ms: e multiens) (elt: e): bool = match ms with Vide -> false Multiset(g,(e,card),d) -> let appg = appartientmultiens g e in let appd = appartientmultiens d e in if (elt=e) then true else appd appg;; 5. (5 points) occurrencesmultiens: e e multiens int calcule le nombre d occurrences d un élément dans un multi-ensemble. let rec occurencesmultiens2 (ms: e multiens) (elt: e): int = match ms with Vide -> 0 Multiset(g,(e,card),d) -> if (elt=e) then card else if elt<e then occurencesmultiens g e else occurencesmultiens d e ;; 6. (10 points) ajoutemultiens: e mutielt e multiens e multiens ajoute une ou plusieurs occurrences d élément à un multi-ensemble. let rec ajoutmultiens (elt: e) (ms: e multiens): e multiens = match ms with Vide -> Multiset(Vide,(elt,1),Vide) Multiset(g,(e,card),d) -> if (elt=e) then Multiset(g,(e,card+1),d) else if (elt>e) then Multiset(g,(e,card), ajoutmultiens e d) else Multiset(ajoutmultiens e g,(e,card), d);; 7. (15 points) construitmultiens: e list e multiens prend une liste d éléments et construit le multi-ensemble associé (7 points) Écrire une version sans utiliser List.fold_right ou List.fold_left. let rec construitmultiens (l: a list) : e multiens = match l with [] -> Vide x::r -> ajoutmultiens x (construitmultiens r);; (8 points) Écrire une version en utilisant List.fold_right ou List.fold_left. let construitmultiens2 (l: a list) : e multiens = List.fold_right ajoutmultiens l Vide;; 8. (10 points) On se pose le problème de trouver la liste des éléments qui apparaissent exactement deux fois dans une liste donnée (problème dont on a donné une solution dans l exercice 3). On vous demande ici de proposer une autre solution en utilisant des fonctions définies précédemment dans cet exercice. let deuxocc3 (l: a list): a list = let rec auxdeuxocc3 (ab : e multiens ): a list= let ab = construitmultiens l in match ab with Vide -> [] Multiset(g,(e,card),d) -> let listeoccur2 = (auxdeuxocc3 g) @ (auxdeuxocc3 d) in if card = 2 then e::listeoccur2 else listeoccur2 in auxdeuxocc3 (construitmultiens l);; BONUS (20 points) : supprimemultiens: e multielt e multiens e multiens supprime n occurrences d élément (n étant défini par le multielt) d un multi-ensemble. 5