Exercice Ocaml programmation fonctionnel, Exercises of Programming Paradigms

Language de programmation fonctionnel

Typology: Exercises

2019/2020

Uploaded on 06/19/2020

techno-pro
techno-pro 🇹🇳

1 document

1 / 12

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
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+ . . . +nsi n est positif,
et 0 n est egatif ou nul.
# sommeN (0);;
-:int=0
# sommeN (-1);;
-:int=0
# sommeN 1;;
-:int=1
# sommeN 3;;
-:int=6
2. eroulez manuellement ces appels. Rappel : un d´eroulement ecursif peut se faire manuellement en
rempla¸cant chaque appel `a la fonction par son corps o`u le param`etre formel est remplac´e par le pa-
ram`etre de l’appel, puis en 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 th en 0 e l s e . . .
= 0
sommeN ( 0 ) =
= i f 0<=0 t he n 0 e l s e . . .
= 0
sommeN ( 3 ) =
= i f 3<=0 t he n 0 e l s e 3 + sommeN( 2 )
= 3 + sommeN (2 )
= 3 + ( i f 2<=0 t he n 0 e l s e 2 + sommeN ( 2 1))
= 3 + ( 2 + sommeN (1 ) )
= 3 + ( 2 + ( i f 1<=0 th en 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 th e n 0 e l s e 0 + sommeN ( 1) )
= 3 + 2 + 1 + 0
= 6
3.
1
pf3
pf4
pf5
pf8
pf9
pfa

Partial preview of the text

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);;

  • : int = 0

sommeN (-1);;

  • : int = 0

sommeN 1;;

  • : int = 1

sommeN 3;;

  • : int = 6

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);;

  • : bool = true

motI ("abcd456",5);;

  • : bool = false

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";;

  • : bool = true

est_mot "6B7s";;

  • : bool = false

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";;

  • : bool = true

est_mot "687678";;

  • : bool = false

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’;;

  • : bool = false

voyelle ’i’;;

  • : bool = true

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;;

  • : bool = false

opArith ’+’;;

  • : bool = false

(* 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";;

  • : bool = true

reponse "non";;

  • : bool = false

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;;

  • : bool = true

bissextile 1900;;

  • : bool = false

bissextile 2004;;

  • : bool = true

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;;

  • : int = 29

joursMois 2 2005;;

  • : int = 28

joursMois 3 2006;;

  • : int = 31

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;;