INF121: Algorithmique et Programmation Fonctionnelle Cours 4: Fonctions récursives Année 2013-2014
Les précédents épisodes de INF 121 Types de base : Type Opérations Constantes bool not, &&, true, false int +,-,*,/,mod,......,-1, 0, 1,... float +.,-.,*.,/. 0.4, 12.3, 16., 64. char lowercase, code,... a, u, A,... if... then... else... expression conditionnelle identificateurs (locaux et globaux) définition, utilisation et composition de fonctions types avancés : synonyme, énuméré, produit, somme filtrage et pattern-matching 1 / 19
Plan Retour sur les fonctions Fonctions récursives Terminaison
Définition de fonctions en OCaml Rappels let fct_name (p1:t1) (p2:t2)... (pn:tn) : t = expr Example let max2 (x: int) (y: int) : int = if x > y then x else y Une fonction OCaml des paramètres formels (optionnels) : x et y une valeur = l expression correspondant au corps de la fonction (qui permet de calculer le résultat) if x > y then x else y un type, qui dépend : du type des paramètres de la fonction du type du résultat un nom (optionnel) : max2 int int int 2 / 19
Appel de fonctions en OCaml Rappels Example (max2 5 9) (les parenthèses peuvent parfois être omises) (max2 (min2 3 8) 9) (max2 (5 4) (8 + 3)) Plus généralement : let fct_name (p1:t1) (p2:t2)... (pn:tn) : t = expr le type de fct_name est t1 t2... tn t appel de la fonction fct_name à n paramètres : (fct_name e1 e2... en) où : e1,...,en sont les paramètres effectifs chaque paramètre effectif ei a pour type ti Le type de (fct_name e1 e2... en) est t 3 / 19
Retour sur le type des fonctions en OCaml C est à cause de Curry (Haskell)! Fonction partielle Une fonction à n 1 paramètres let fct_name (p1:t1) (p2:t2)... (pn:tn) : t = expr peut être vue comme une fonction : à 1 seul paramètre dont le résultat est une fonction à n 1 paramètres : (fct_name e1) (e2... en) On parle de fonction partielle ou encore de curryfication Exemple : (max2 3) fonction à un paramètre (int int) (max2 3) x renvoie le maximun entre 3 et x Remarque : une fonction sans paramètre est une constante let f = 3 ;; f ;; (* vaut 3 *) 4 / 19
Fonctions: SPECIFICATION et IMPLEMENTATION Il est très important de distinguer 2 concepts/étapes lors de la définition d une fonctions (et d un programme en général) Spécification: Une description de ce qui est attendu à un certain niveau d abstraction doit être suffisamment précis proche d une description mathématique peut utiliser des exemples pertinents Un contrat Entrées Consiste en: Fonction 1 description 1 signature (son type) des exemples Sorties Implémentation: La description de comment le réaliser Entrées Fonction Sorties le code OCaml Définir une fonction : Spécification PUIS Implémentation 5 / 19
Plan Retour sur les fonctions Fonctions récursives Terminaison
A propos de récursivité Qu est-ce que la récursivité, qu est-ce qu une définition récursive? Example (Quelques objets récursifs) un+1 = F (un ) Images sous licence Creative Common Fonctions récursives I exécuter plusieurs fois une même fonction sur des données différentes pour produire un résultat : f (f (f (...f (x0 )...))) I généralisation des suites récurrentes I un élément de base de la programmation fonctionnelle... I et beaucoup (beaucoup) d autres applications en informatique! 6 / 19
Fonctions récursives en OCaml Un 1er exemple Example (Factorielle) { U0 = 1 U n = n U n 1, n 1 { 0! = 1 n! = n (n 1)!, n 1 Cette définition fournit un résultat pour tout entier 0 : elle est bien fondée contre-exemple : U 0 = 1 et U n = n U n+1, n 1 Il est important de vérifier qu une définition est bien fondée Example (Définir la fonction factorielle en OCaml) let rec fact (n:int):int = if n=0 then 1 else n fact(n 1) let rec fact (n:int):int = match n with 0 1 n n fact(n 1) 7 / 19
Définir une fonction récursive Spécification: description, signature, exemples, et équations de récurrence Implémentation: code de la fonction en OCaml let rec fct_name (p1:t1) (p2:t2)... (pn:tn):t = expr où expr peut contenir zéro, un ou plusieurs appels à fct_name. On distinguera différentes sous-expressions de expr : les cas de base : aucun appel à fct_name les cas récursifs : un ou plusieurs appel(s) à fct_name Les règles de typage sont les mêmes que pour le fonctions non récursives. Remarque t1,.., tn peuvent être des types quelconques Une fonction récursive ne peut pas être anonyme 8 / 19
Exemples de définitions de fonctions récursives Example (Somme des entiers de 0 à n) description + profil + exemples 0 i = 0 i=0 n i i=0 = n + n 1 i=0 i si n > 0 let rec sum (n : int) : int = match n with 0 0 n n + sum (n 1) Example (Division entière) description + profil + exemples a/b = { 0 si a < b 1 + (a b)/b si b a let rec div (a : int) (b: int) : int = if a < b then 0 else 1 + div (a b) (b) 9 / 19
Essayons... Exercice : reste de la division entière Définir une fonction qui calcule le reste de la division entière Exercice : suite de Fibonacci Implémenter une fonction qui renvoie le n ieme terme de Fibonacci où n est donné comme paramètre. La suite de Fibonnaci est défini comme : { 1 si n = 0 ou n = 1 fib n = fib n 1 + fib n 2 si n > 1 10 / 19
Appel et exécution d une fonction récursive Déplier le corps de la fonction ré-écriture Exemple : arbre des appels des fonctions factorielle et fibonacci fact(3) =3 2=6 3 2 1 = 2 fib(4)=3+2=5 fact(2) fib(3)=2+1=3 fib(2)=1+1=1 2 fact(1) 1 1 = 1 fib(2)=1+1=2 fib(1)=1 fib(1)=1 fib(0)=1 1 1 fib(1)=1 fib(0)=1 fact(0) = 1 : ré-écriture des appels générés et suspension des opérations en cours : évaluation (en ordre inverse) des opérations suspendues En OCaml: directive #trace DEMO: Tracer une fonction 11 / 19
Entrainement (suite) Exercice: la fonction puissance (2 versions) { x 0 = 1 x n = x x n 1 si 0 < n x 0 = 1 x n = (x x) n/2 si n est pair x n = x (x x) n 1 2 si n est impair Donnez 2 implémentations de la fonction power: int int int en vous basant sur ces 2 définitions équivalentes. Quelle est la différence entre ces deux versions? 12 / 19
Fonctions mutuellement récursives Sur un exemple Récursivité directe : appels récursifs à une seule fonction Qu en est-il d une fonction f qui appelle g qui appelle f qui appelle... fonctions mutuellement récursives (récursivité croisée) Example (Est-ce qu un nombre est pair ou impair?) Comment déterminer si un entier est pair ou impair sans utiliser /,, mod (donc en utilisant uniquement et =)? n N est impair si n 1 est pair n N est pair si n 1 est impair 0 est pair 0 n est pas impair let rec even (n:int):bool = if n=0 then true else odd (n 1) and odd (m:int):bool = if m=0 then false else even (m 1) DEMO: pair et impair, récursivité croisée 13 / 19
Fonctions mutuellement récursives Généralisation let rec fct1 [parametres+type resultat] = expr_1 and fct2 [parametres+ type resultat] = expr_2... and fctn [parametres+type resultat] = expr_n où expr_1, expr_2,..., expr_n peuvent appeler fct1, fct2,..., fctn 14 / 19
Plan Retour sur les fonctions Fonctions récursives Terminaison
Terminaison Pensez vous que l exécution de cette fonction termine (la fonction de McCarthy)? { n 10 si n > 100 mac(n) = mac(mac(n + 11)) si n 100 Et celles-ci? La fonction puissance { x 0 = 1 x n = x x n 1 si 0 < n La fonction factorielle fact(0) 1 fact(1) = 1 fact(n) = fact(n+1) n+1 Il est fondamental de savoir décider si une fonction termine ou non Peut-on caractériser la terminaison sur l arbre des appels? 15 / 19
Comment prouver qu une fonction récursive termine? En utilisant une mesure... Theorem Toute suite positive strictement décroissante converge Méthode générale pour prouver qu une fonction termine Dériver une mesure M(n) t.q. : M(n) est positive n dépend des paramètres de la fonction M(n) décroit strictement entre deux appels récursifs la suite M(n) converge vers une valeur m 0 associée à un cas de base Example (Terminaison de la fonction somme) let rec somme (x : int) : int = match x with 0 0 x x + sum (x 1) Mesure : Définissons M(n) = x, M(n) N M(n) > M(n 1) puisque x > x 1 M(n) converge et atteint la valeur 0. 16 / 19
Terminaison de quelques fonctions Exercice: trouver les mesures Prouvez que les fonctions factorielle, puissance, quotient, reste terminent... 17 / 19
Terminaison de quelques fonctions factorielle et puissance Terminaison de fact: let rec fact (n:int):int = match n with 0 1 n n fact(n 1) Terminaison de power: let rec power (a:float) (n:int):float = if (n=0) then 1. else (if n>0 then a. power a (n 1) else 1. /. (power a (n 1)) ) Définissons M(fact n) = n N M(fact n) > M(fact (n 1)) puisque n > n 1 M(fact n) converge vers 0. Définissons M(power a n) = n M(power a n) N (d après la spec) M(power a n) > M(power a (n 1)) M(power a n) converge vers 0. 18 / 19
Terminaison de quelques fonctions let rec quotient (a:int) (b:int):int = if (a<b) then 0 else 1 + quotient (a b) b let rec reste (a:int) (b:int):int = if (a<b) then a else reste (a b) b Terminaison de quotient et reste: Définissons M(X a b) = a M(X a b) N (d après la spec) M(X a b) > M(X (a b) b) puisque b > 0 M(X a b) converge vers une valeur a 0 telle que a 0 < b. où X {quotient, reste} 19 / 19