Programmation fonctionnelle Cours 6 : la récursivité Licence 1 Année 2018 2019 Par N. VINCENT La longueur d'une liste Fonction qui calcule la longueur d'une liste : List.length L = [] L = [ 34 ] L = [3 ; 2 ; 5 ; 2 ; 4 ; 9 ; 7 ; 3 ] = h :: t h contribue de 1 t contribue de (longueur de t) Si longueur de t était connue long, on aurait 1 + long Hypothèse : savoir calculer la longueur de t Licence 1 - programmation fonctionnelle 2 La résolution d'un problème Classiquement Pour résoudre un problème - on applique une formule - on essaie de transformer le problème en des problèmes plus simples que l on sait résoudre Méthode récursive Il n est pas nécessaire de savoir résoudre explicitement le problème posé Licence 1 - programmation fonctionnelle 3 La résolution récursive Pour résoudre un problème - on transforme le problème P en un problème plus simple P - on doit faire le lien entre la solution de P et celle de P L'algo final de résolution de P comporte un appel à la fonction que l'on veut définir concernant le problème P Licence 1 - programmation fonctionnelle 4 Calcul de la longueur d une liste Une solution : la fonction List.length Algo - si la liste est vide, la longueur est 0 - sinon la longueur est 1 de plus que la longueur du reste de la liste L'algo comporte - Une solution dans un cas simple - un appel à la fonction que l'on veut définir dans un cas plus simple l = h::t Licence 1 - programmation fonctionnelle 5 La résolution d'un problème Actions et calculs simples Fonction qui teste tous les éléments d'une liste : List.for_all Algo - si la liste est vide le résultat est vrai - sinon le résultat est le cumul entre le résultat du test de l'élément de tête et le résultat de la fonction appliquée au reste Licence 1 - programmation fonctionnelle 6 1
Le principe de résolution On a cherché à décomposer le problème Une action simple pour résoudre le problème quand l'argument est simple Le même problème sur une donnée plus réduite C'est une méthode récursive (la fonction s appelle elle-même) On définit une fonction récursive #let rec f x =... Licence 1 - programmation fonctionnelle 7 Fonction récursive Schéma de base #let rec f x = Si condition sur x Alors résultat facile à calculer Sinon lien entre le résultat sur les données plus simples et le résultat sur x Les difficultés : - définir le cas simple (condition sur x résultat) - trouver le lien avec les cas plus simples ramenant au cas simple Licence 1 - programmation fonctionnelle 8 Longueur d'une liste L Exemple Liste dont la longueur est la plus petite Liste vide Problème plus simple que L (qui rapproche de la liste vide) L=h::t (longueur t < longueur L) Lien entre la longueur de L et longueur de t longueur L = 1 + longueur t Licence 1 - programmation fonctionnelle 9 Calculer la longueur d'une liste # let rec long l = if l = [] then 0 else 1 + long (List.tl l) ;; # long ['a' ; 'a' ; 'a'] ;; - : int = 3 # let rec long l = match l with [] -> 0 h::t -> 1 + long t ;; # long ['a' ; 'a' ; 'a'] ;; - : int = 3 Licence 1 - programmation fonctionnelle 10 Exemple Calculer la longueur d'une liste # let rec long l = match l with [] -> 0 h::t -> 1 + long t ;; long l = 1 + long [2 ; 3; 4] long [2 ; 3; 4] = 1 + long [3; 4] long [3; 4] = 1 + long [4] long [4] = 1 + long [] long [ ] = 0 l = 1 :: [2 ; 3; 4] long l = 4 long [2 ; 3; 4] = 3 long [3; 4] = 2 long [4] = 1 Licence 1 - programmation fonctionnelle 11 Liste paire # let p x = x mod 2 = 0 ;; val p : int -> bool = <fun> # let parite l = List.for_all p l ;; val parite : int list -> bool = <fun> # parite [2;3;2] ;; - : bool = false # parite [-2 ; 6 ; 2 ; 10] ;; - : bool = true # let par l = let p x = x mod 2 = 0 in List.for_all p l ;; val par : int list -> bool = <fun> # par [2;3;2] ;; - : bool = false # par [-2 ; 6 ; 2 ; 10] ;; - : bool = true Licence 1 - programmation fonctionnelle 12 2
Version récursive liste paire Liste vide Cas le plus simple Liste avec un élément Critère de simplicité Longueur de la liste Cas «plus simple» l = h::t l -> t Lien entre cas général et cas «plus simple» pair(l) = p(h) et pair(t) Licence 1 - programmation fonctionnelle 13 Version récursive liste paire pair(l) = p(h) et pair(t) # let p x = x mod 2 = 0 ;; val p : int -> bool = <fun> # let rec f l = if l = [] then true else match l with h::t -> p h && f t [] -> true ;; val f : int list -> bool = <fun> let rec f l = match l with [] -> true # f [2 ; 3 ; 2] ;; h::t -> p h && f t ;; - : bool = false # f [-2 ; 6 ; 2 ; 10] ;; Licence 1 - programmation fonctionnelle - : bool = true 14 Cas le plus simple Critère de simplicité 0 1 Entier le plus petit Cas «plus simple» n n-1 Lien entre cas général et cas «plus simple» n! = n * (n-1)! 2! = 2 Licence 1 - programmation fonctionnelle 15 n! = n (n-1)! 1! = 1 # let rec fact2 n = if n = 1 then 1 else n * fact2(n-1) ;; val fact2 : int -> int = <fun> # fact2 0 ;; # fact2 1 ;; # fact2 2 ;; # fact2 5 ;; 20 Stack overflow during evaluation (looping recursion?). Licence 1 - programmation fonctionnelle 16 # fact (-3) ;; # fact 1 ;; # fact 0 ;; # fact 2 ;; # fact 5 ;; 20 Stack overflow during evaluation (looping recursion?). Licence 1 - programmation fonctionnelle 17 # let rec fact n = assert (n >= 0) ; if n = 0 then 1 else n * fact(n-1) ;; # fact (-3) ;; # fact 1 ;; # fact 0 ;; # fact 2 ;; # fact 5 ;; 20 Exception: Assert_failure ("", 1, 17). Licence 1 - programmation fonctionnelle 18 3
fact 4 4*fact 3 Le processus de calcul 4*3*fact 2 4*3*2*fact 1 4*3*2*1*fact 0 4*3*2*1*1 On peut demander à voir la suite des appels durant l exécution Licence 1 - programmation fonctionnelle 19 Voir le processus de calcul # fact 4 ;; # #trace fact ;; fact is now traced. # #untrace fact ;; fact is no longer traced. # fact 4 ;; 4 fact <-- 4 fact <-- 3 fact 4 fact <-- 2 4*fact 3 fact <-- 1 fact <-- 0 fact --> 1 fact --> 1 fact --> 2 fact --> 6 4*3*2*1*1 fact --> 24 4 Licence 1 - programmation fonctionnelle 20 4*3*fact 2 4*3*2*fact 1 4*3*2*1*fact 0 Tracer une fonction Ce sont les appels qui sont tracés # let f x = x*x + 3 ;; val f : int -> int = <fun> # trace f ;; Error: Unbound value trace # #trace f ;; f is now traced. # f 4 ;; f <-- 4 f --> 19 9 # let g x = x * (x + 3) ;; val g : int -> int = <fun> # #trace g ;; g is now traced. # g 4 ;; g <-- 4 g --> 28 8 Licence 1 - programmation fonctionnelle 21 Les points importants Méthode récursive de résolution de problème Cas limite Critère pour arriver au cas limite Lien entre deux calculs Définition #let rec f x =... Licence 1 - programmation fonctionnelle 22 Fonction récursive Schéma de base #let rec f x = Si condition sur x Alors résultat facile à calculer Sinon lien entre le résultat sur les données plus simples et le résultat sur x Les difficultés : - définir le cas simple (condition sur x résultat) - trouver le lien avec les cas plus simples ramenant au cas simple Licence 1 - programmation fonctionnelle 23 Un petit exemple Une fonction qui calcule la somme de deux valeurs # let somme x y = x + y ;; val somme : int -> int -> int = <fun> Valeurs modifiées # let somme x y = f1 f1 f2 x = + f1 f2 x y + ;; f2 y ;; Error: val somme Unbound : 'a -> value 'b -> f1('a -> int) -> ('b -> int) -> int = <fun> # let f x a = a * x ;; val f : int -> int -> int = <fun> # let ff a x = a * x ;; val ff : int -> int -> int = <fun> par des fonctions paramétrées par des fonctions paramétrées x -> 3x ; x -> 3x+5 # let g x a b = a * x + b ;; val g : int -> int -> int -> int = <fun> # let gg a b x = a * x + b ;; val gg : int -> int -> int -> int = <fun> # let nvsom x y = somme x y# ff let 3 nvsom gg 3 5 ;; x y = somme x y (ff 3) (gg 3 5) ;; val nvsom : int -> int -> int = <fun> Error: This function is applied to too many arguments; maybe you forgot a `;' Licence 1 - programmation fonctionnelle 24 4
Exemple (suite) # let somme x y f1 f2 = f1 x + f2 y ;; val somme : 'a -> 'b -> ('a -> int) -> ('b -> int) -> int = <fun> # somme 23 54 ;; - : (int -> int) -> (int -> int) -> int = <fun> # let somme f1 f2 x y = f1 x + f2 y ;; val somme : ('a -> int) -> ('b -> int) -> 'a -> 'b -> int = <fun> # somme (ff 3) (gg 3 5) ;; - : int -> int -> int = <fun> Licence 1 - programmation fonctionnelle 25 5