Download Exercice Ocaml programmation fonctionnel and more Exercises Programming Paradigms in PDF only on Docsity!
NFP119 : corrig´e feuille d’exercices 2
Mar´ıa-Virginia Aponte
10 octobre 2013
Exercice 1
1. Testez cette fonction en Ocaml pour les appels suivants : sommeN(0), sommeN(-1), sommeN(1), sommeN(3).
L’appel SommeN(n) calcule la somme des n premiers nombres positis 1 + 2 + 3 +... + n si n est positif,
et 0 n est n´egatif ou nul.
sommeN (0);;
sommeN (-1);;
sommeN 1;;
sommeN 3;;
2. D´eroulez manuellement ces appels. Rappel : un d´eroulement r´ecursif peut se faire manuellement en
rempla¸cant chaque appel a la fonction par son corps ou le param`etre formel est remplac´e par le pa-
ram`etre de l’appel, puis en r´ealisant les op´erations correspondantes (en mimant l’ex´ecution). Exemples
pour la factorielle dans les transparents du cours.
sommeN (−1)= = i f − 1 <=0 then 0 e l s e... = 0
sommeN ( 0 ) = = i f 0 <=0 then 0 e l s e... = 0
sommeN ( 3 ) = = i f 3 <=0 then 0 e l s e 3 + sommeN ( 2 ) = 3 + sommeN ( 2 ) = 3 + ( i f 2 <=0 then 0 e l s e 2 + sommeN (2 −1)) = 3 + ( 2 + sommeN ( 1 ) ) = 3 + ( 2 + ( i f 1 <=0 then 0 e l s e 1 + sommeN (1 −1)) = 3 + 2 + ( 1 + sommeN ( 0 ) ) = 3 + 2 + 1 + sommeN ( 0 ) = 3 + 2 + 1 + ( i f 0 <=0 then 0 e l s e 0 + sommeN ( −1)) = 3 + 2 + 1 + 0 = 6
4. Cette fonction termine-t-elle dans tous les cas? Pouvez vous dire pourquoi?
Oui.
Si n ≤ 0 elle termine car il n’y a plus d’appel r´ecursif.
Si n > 0 elle termine car chaque appel r´ecursif se fait sur le param`etre n − 1 qui est plus petit que n.
Le cas de base o`u n = 0 est atteint en un n ombre fini d’´etapes.
Exercice 2
Terminaison
Consid´erez les d´efinitions de fonctions suivantes. Ces fonctions terminent-elles toujours? Donnez un d´eroulement
r´ecursif manuel pour les appels donn´es plus bas, puis testez ces appels dans la boucle int´eractive d’Ocaml.
1. # let rec fact n =
if n=1 then 1 else n*fact(n-1);;
Testez cette fonction avec les appels (fact 2), (fact 0) et fact (-1). Quel est le probl`eme? Donnez une
correction simple.
Solution : Cette fonction termine pour les arguments positifis, mais ne termine pas si n est n´egatif ou
nul.
fact(0)= = if 0=1 then 1 else 0 * fact(0-1) = 0 * fact(-1) = 0 * (if -1=1 then 1 else (-1)* fact(-2)) = 0 * (-1)* fact(-2) = 0 * (-1) * fact(-2) = 0 * (-1) * (-2) * fact(-3) * ...
Une correction simple, consiste a changer la condition du cas de base de maniere `a capturer ´egalement
ces deux cas-l`a. Cela donne :
let rec fact n =
if n<=1 then 1 else n*fact(n-1);;
2. # let rec fact n =
if n<=1 then 1 else n*fact(n+1);;
Testez cette fonction avec les appels (fact 0) et (fact 1). Quel est le probl`eme? Donnez une correction
simple.
Solution : Cette fonction ne termine que si l’argument pass´e est plus petit ou ´egal `a 1 ; S’il est plus
grand, la fonction boucle dans les appels r´ecursifs : on fait un appel r´ecursif pour un argument `a chaque
fois plus grand. Comme le cas de base est 1 ou plus petit, on ne s’arrˆete jamais.
fact 2 ⇒ if 2<=1 then 1 else 2fact (2+1) ⇒ 2 * (fact(3)) ⇒ 2 * (if 3<=1 then 1 else 3factorielle (3+1)) ⇒ 2 * (3factorielle (4)) ⇒ 3 * (2 (if 4<=1 then 1 else ...) ⇒
...
Exercice 4
Question 1
R´ecursivit´e, programmation, fonctions d’ordre sup´erieur Consid´erez les fonction suivantes :
let lettre c = (’a’ <= c && c<= ’z’) or (’A’<=c && c<= ’Z’);;
let rec motI (s,i) = if i<=0 then true else (lettre (s.[i])) && motI (s,(i-1));; val motI : string * int -> bool =
motI ("abcd456",3);;
motI ("abcd456",5);;
1. Donnez un d´eroulement r´ecursif de l’appel motI ("abcd456",3). Testez en Ocaml les exemples pro-
pos´es plus haut.
motI ("abcd456",2) = = if 2<=0 then true else (lettre (s.[2])) && motI (s,(1)) = lettre (s.[2])) && motI(s,(1)) = lettre (’c’) && motI(s,(1)) = true && motI(s,(1)) = motI(s,(1)) = (if 1<=0 then true else (lettre (s.[1])) && motI (s,(0))) = lettre (s.[1])) && motI (s,(0)) = lettre (’b’)) && motI (s,(0)) = true && motI (s,(0)) = motI (s,(0)) = (if 0<=0 then true else ...) = true
motI ("abcd456",5) = = if 5<=0 then true else (lettre (s.[5])) && motI (s,(4)) = lettre (s.[5])) && motI(s,(4)) = lettre (’5’) && motI(s,(4)) = false && motI(s,(4)) = false
2. Donnez une d´efinition math´ematique de cette fonction. Que fait la fonction motI? Quel est le rˆole du
param`etre i?
motI(s, i) =
true si i ≤ 0
lettre(s.[i]) ∧ motI(s, i − 1) si i > 0
L’appel motI(s, i) teste si la sous-chaˆıne de s comprise entre les indices i... 0 est compos´ee uniquement
de lettres. Le parcours d´ebute au caractere d’indice i et termine au caractere d’indice 0. Le rˆole du
param`etre i est de donner l’indice de d´epart du parcours r´ecursif.
3. On souhaite ´ecrire une fonction qui teste si une chaˆıne est compos´ee uniquement de lettres. Proposez
une solution qui incorporent les fonctions lettre et motI. Cette derniere possede 2 arguments mais
un seul change pendant les appels r´ecursifs. Utilisez le sch´ema r´ecursif 2 d´ecrit dans les transparents
du cours.
let est_mot s = let lettre c = (’a’ <= c && c<= ’z’) or (’A’<=c && c<= ’Z’) in let rec motI i = if i<=0 then true else (lettre (s.[i])) && motI (i-1) in motI (String.length s -1) val est_mot : string -> bool =
est_mot "aBc";;
est_mot "6B7s";;
4. Que fait la fonction suivante? Comparez la avec la fonction que vous avez ´ecrite. Comment ´effectue-t-
elle le parcours r´ecursif de la chaˆıne? Donnez un d´eroulement r´ecursif pour ´etayer votre r´eponse.
let est_mot s = let lettre c = (’a’ <= c && c<= ’z’) or (’A’<=c && c<= ’Z’) in let rec parcours i = if i>= String.length s then true else (lettre (s.[i])) && parcours (i+1) in parcours 0;; val est_mot : string -> bool =
est_mot "abc";;
est_mot "687678";;
Solution : La fonction est mot teste si une chaˆıne de caract`eres est uniquement compos´ee de lettres.
Elle utilise la fonction locale r´ecursive parcours, qui parcourt la chaˆıne du premier au dernier indice
en testant si le caractere visit´e est une lettre. Contrairementa la fonction motI, le parcours d´ebute ici
au premier caract`ere, et c’est pourquoi le cas d’arrˆet teste si i>=String.length s, et le cas r´ecursif
fait un appel sur l’indice suivant (i + 1).
5. La directive trace permet de tracer tous les appels d´eclench´es par une fonction r´ecursive. Cela ne
marche pas pour les fonctions r´ecursives locales. Nous allons donc d´efinir parcours globalement afin
de tester plusieurs appels. Attention : il faut d´eclarer la fonction `a tracer par # trace nom-fonction,
ou l’on ´ecrit explicitement un caractere
let lettre c = (’a’ <= c && c<= ’z’) or (’A’<=c && c<= ’Z’) ;;
val lettre : char -> bool =
let rec parcours i =
if i>= String.length s then true else (lettre (s.[i])) && parcours (i+1);; val parcours : int -> bool =
#trace parcours;;
parcours is now traced.
parcours 0;;
parcours <-- 0 parcours <-- 1
l e t l e t t r e c = ( ’ a ’ <= c && c<= ’ z ’ ) o r ( ’A’<=c && c<= ’ Z ’ ) ; ;
v a l l e t t r e : c h a r −> b o o l =
t e s t e C h a i n e ” b o n j o u r ” l e t t r e ; ;
− : b o o l = t r u e
t e s t e C h a i n e ” bon67 ” l e t t r e ; ;
− : b o o l = f a l s e
l e t e s t m o t s = t e s t e C h a i n e s l e t t r e ; ;
v a l e s t m o t : s t r i n g −> b o o l =
e s t m o t ” bon67 ” ; ;
− : b o o l = f a l s e
e s t m o t ” b o n j o u r ” ; ;
− : b o o l = t r u e
3. Ecrivez une fonction est nombre qui teste si une chaˆıne ne contient que des nombres par un appel `a
testeChaine.
l e t nombre c = ’ 0 ’ <= c && c<= ’ 9 ’ ; ;
v a l nombre : c h a r −> b o o l =
l e t e s t n o m b r e s = t e s t e C h a i n e s nombre ; ;
v a l e s t n o m b r e : s t r i n g −> b o o l =
e s t n o m b r e ” 8 7 5 0 ” ; ;
− : b o o l = t r u e
Exercice 5
Polymorphisme, typage.
Pour les fonctions suivantes, tentez de d´ecouvrir leur type, puis comparez votre r´eponse avec celle donn´ee
par Ocaml.
let f(x,y) = (y,x);;
val f : ’a * ’b -> ’b * ’a =
let f(x,y) = x^y;;
val f : string * string -> string =
let f(x,y) = (x+y, x-y, x*y);;
val f : int * int -> int * int * int =
Ici, x et y ne peuvent pas ˆetre `a la fois de type int et string.
let f(x,y) = (x+y, x^y);;
This expression has type int but is here used with type string
Ici, appliquer f ´equivaut `a appliquer fst. La fonction f devient une soret d’alias pour fst. Elles ont donc
des types identiques.
let f x = fst x;;
val f : (’a * ’b) -> ’a =
Ici, x est de type quelconque. Par ailleurs, fst est appliqu´ee sur y, et donc celui-ci doit n´ecesairement avoir
le type d’une paire.
let f(x,y) = (x, fst y);;
val f : ’a * (’b * ’c) -> ’a * ’b =
Mˆeme raisonnement qu’avant mais cette fois sur x et y.
let f(x,y) = (fst x, fst y);;
val f : (’a * ’b) * (’c * ’d) -> ’a * ’c =
Mˆeme raisonnement sur x et y. De plus, les premi`eres composantes de x et y sont n´ecessairement de type
int, ainsi que le r´esultat de la fonction.
let f(x,y) = fst x + fst y;;
val f : (int * ’a) * (int * ’b) -> int =
let g x y = (x,y);;
val g : ’a -> ’b -> ’a * ’b =
let g x y = (x,y,x&&y);;
val g : bool -> bool -> bool * bool * bool =
Exercice 6
Filtrage.
Lorsqu’il y a erreur, expliquez les messages affcih´es par Ocaml, et proposez une solution. Pour les fonctions
sans erreur, testez-les sur plusieurs exemplez et expliquez ce qu’elles font.
Solution : La fonction voyelle teste si un caract`ere est une voyelle.
voyelle ’b’;;
voyelle ’i’;;
La fonction opArith est d´efinie de sorte que le premier cas filtre tous les argument possibles de la fonction.
Du coup, elle est polymorphe et accepte n’importe quel type d’argument. De plus, elle r´epond false toujours.
La solution est d’inverser les deux filtres :
#let opArith c = match c with _ -> false;; | ’+’| ’-’ | ’*’ | ’/’ -> true val opArith : ’a -> bool =
opArith 1;;
opArith ’+’;;
(* Correction *)
#let opArith c = match c with ’+’| ’-’ | ’*’ | ’/’ -> true
reponse "non";;
Exception: Match_failure ("", 2, 0).
La solution est de compl´eter le filtrage :
let reponse r =
match r with"oui" -> true | "Oui" -> true | "OUI" -> true | _ -> false;; val reponse : string -> bool =
reponse "oui";;
reponse "non";;
Exercice 7
D´efinir un type enregistrement date pour mod´eliser une date compos´ee d’un num´ero de jour, d’un num´ero
de mois et d’une ann´ee.
type date = {jour:int; mois:int; annee: int};; type date = { jour : int; mois : int; annee : int; }
Ecrivez ensuite :
1. Un exemple de date dans la variable aujourd’hui.
2. Une fonction bissextile qui teste si une ann´ee est bissextile.
let bissextile a =
(a mod 4 =0) && (not (a mod 100 = 0) or (a mod 400 = 0));; val bissextile : int -> bool =
bissextile 2000;;
bissextile 1900;;
bissextile 2004;;
3. Une fonction joursMois qui calcule le nombre de jours dans un mois pour une ann´ee donn´ee.
let joursMois m a =
match m with 4 | 6 | 9 | 11 -> 30 | 2 -> if bissextile a then 29 else 28 | _ -> 31
val joursMois : int -> int -> int =
joursMois 2 2004;;
joursMois 2 2005;;
joursMois 3 2006;;
4. En utilisant les deux fonctions pr´ec´edentes, ´ecrire une fonction lendemain qui ´etant donn´e une date
suppos´ee correcte calcule la date du lendemain.
let lendemain d =
if d.jour < joursMois d.mois d.annee then {jour= d.jour+1; mois=d.mois; annee = d.annee} else if d.mois < 12 then {jour= 1; mois=d.mois+1; annee = d.annee} else {jour= 1; mois= 1; annee = d.annee+1} val lendemain : date -> date =
#let aujourd’hui = {jour=27; mois=2; annee = 2006};; val aujourd’hui : date = {jour = 27; mois = 2; annee = 2006}
lendemain aujourd’hui;;
- : date = {jour = 28; mois = 2; annee = 2006}
Exercice 8
D´efinissez un type employe caract´eris´e par un nom, un salaire, et une date d’entr´ee dans l’entreprise.
1. Ecrivez une fonction qui en prenant deux employ´es, renvoyer celui dont le salaire est le plus ´el´ev´e.
2. Ecrivez une fonction pour calculer l’anciennet´e d’un employ´e en mois et ann´ees (par exemple 2 mois,
ou 1 an et 2 mois).
#type employe = {nom: string; salaire: float; dateArrivee: date};;
#type duree = {n_mois: int; n_annees: int};;
#let anciennete aujourd’hui {dateArrivee = e} = let soustrait_dates d1 d2 = if d1.annee = d2.annee then {n_mois = d1.mois - d2.mois; n_annees = 0} else if d1.mois = d2.mois then {n_mois = 0; n_annees = d1.annee-d2.annee} else if d1.mois > d2.mois then {n_mois = d1.mois - d2.mois; n_annees = d1.annee-d2.annee} else {n_mois = (12-d2.mois)+ d1.mois; n_annees = d1.annee-d2.annee-1} in soustrait_dates aujourd’hui e;;
val anciennete : date -> employe -> duree =
let sep2000 = {jour =5; mois=9; annee = 2000};;
val sep2000 : date = {jour = 5; mois = 9; annee = 2000}
let martin = {nom = "Martin"; salaire = 2200.50; dateArrivee=sep2000};;
val martin : employe = {nom = "Martin"; salaire = 2200.5; dateArrivee = {jour = 5; mois = 9; annee = 2000}}
anciennete aujourd’hui martin;;