Solution - Verimag
Transcription
Solution - Verimag
C ORRIGÉ INF121 : C ONTRÔLE FINAL (3h00)
Mai 2012
D OCUMENTS 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
|([],[])
-> true
|[] -> 0
|(x::y,[]) -> false
|c::d -> 1 + (f1 d);;
|([],z::t) -> true
|(u::v,w::t)-> if u=w then f4 v t
f1 calcule le nombre d’élément de la liste b.
else f4 (u::v) t;;
val f1 : α list → int = <fun>
f4 détermine si la liste p est un sous sequence de
2. let rec f2 a r = match a with
la liste q.
|[] -> r
|c::d -> f2 d (1 + r);;
val f4: α list → α list → bool =<fun>
f1 calcule le nombre d’élément de la liste b plus
5. let rec f5 h i j = match i with
r.
|[] -> j
val f2 : α list → int → int = <fun>
|x::y -> h x (f5 h y j);;
3. let rec f3 a r = match a with
f5 est le code de la fonction fold_right, qui
|[] -> 0
accumule les résultats de l’application successive
|c::d -> f3 d (1 + r);;
de la fonction h sur la liste i avec comme premier
f3 renvoie toujours la valeur 0.
élément j.
val f3 : α list → int → int = <fun>
val f5 : (α → β → β) → α list → β → β =
4. let rec f4 p q = match (p, q) with
<fun>
Exercice 2 : Modélisation (15 points)
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
|[] -> 0
|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
|[] -> 0
|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.
– Réalisation :
Donner explicitement :
– Spécification :
(a) Équations récursives
(a) Profil
(b) Terminaison
(b) Exemples
(c) Code OCaml
– Spécification :
(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 (e1 , n1 ) et (e2 , n2 ) sont de type ’e multielt, (e1 , n1 ) ≤ (e2 , n2 ) si et seulement si e1 ≤ e2 .
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