FP Plan du chapitre 2 A. Notion de récursivité B. Récursivité dans ℕ C. Récursivité dans séq(α) inf201 : algorithmique et programmation fonctionnelle Récursivité dans séq(α) C.I) Définition récursive de séq(α) a) Notion de séquence Jusqu’à présent : données manipulées unitaires (même si de type complexe). On parle de données scalaires Types produits : nuplets de taille donnée et fixe Comment manipuler des collections de données de taille arbitraire ? → séquences d’éléments inf201 : algorithmique et programmation fonctionnelle FP C.I) Définition récursive de séq(α) a) Notion de séquence Séquence d’éléments : collection de données (éléments) - de même type - de taille quelconque (finie) - imposant un ordre (premier, deuxième ,… dernier) Structure de données fondamentale en info (modélisation) Ex : - liste d’étudiants - “main” dans un jeu de carte - relevés de données scientifiques etc. inf201 : algorithmique et programmation fonctionnelle FP C.I) Définition récursive de séq(α) b) Définition de l’ensemble (type) séq(α) 1- Séquence d’entiers (on pose α=ℤ) Ensemble des séquences sur ℤ définit récursivement par : - l’entité de base Nil (appelé « séquence vide ») - les éléments construits à partir d’un entier et d’une séquence d’entiers : Cons(pr, fin) (constructeur de séquence appelé « ajout à gauche ») Représentation graphique : ... /... cf tableau Représentation mathématique : séq(ℤ) = {Nil} U {Cons(pr,fin) / pr ∈ ℤ, fin ∈ séq(ℤ)} inf201 : algorithmique et programmation fonctionnelle FP C.I) Définition récursive de séq(α) type seqEntier = Nil | Cons of int * seqEntier seqEntier : type somme récursif Exemples : Cons(1,Nil) : séquence (singleton) ne contenant que l’entier 1 Cons(1,Cons(2,Cons(3,Nil))) : séquence contenant 1,2 et 3 (dans cet ordre) /!\ Cons(1,Cons(2,Cons(3,Nil))) ≠ Cons(2,Cons(1,Cons(3,Nil))), la plupart du temps Cons(pr, fin) : séquence constituée d’un premier entier (noté pr) et d'une séquence d’entiers (notée fin) inf201 : algorithmique et programmation fonctionnelle FP C.I) Définition récursive de séq(α) Exemple de fonction sur séq(ℤ) : SPEC longueur d’une séquence Profil longueur : séq(ℤ) → N Sémantique longueur(s) est le nombre d’éléments de s REAL on calcule le « nombre de Cons » (1) longueur(Nil) =0 (2) longueur(Cons(pr,fin)) = 1 + longueur(fin) inf201 : algorithmique et programmation fonctionnelle FP C.I) Définition récursive de séq(α) Longueur d’une séquence : preuve de terminaison On pose mesure(s) = |s| où |s| : cardinal de s (vue comme un ensemble de Cons) 1) mesure à valeur dans ℕ par définition du cardinal 2) mesure décroit strictement entre 2 appels récursifs : mesure (Cons(pr, fin)) > mesure(fin) car |Cons(pr,fin)| = 1 + |fin| et 1 + |fin| > |fin| = mesure(fin) inf201 : algorithmique et programmation fonctionnelle FP C.I) Définition récursive de séq(α) Implémentation : let rec longueur (s:seqEntier) : int = match s with | Nil -> 0 (* d’après éq. (1) *) | Cons(pr,fin) -> 1 + longueur(fin) (* d’après éq. (2) *) # let s1:seqEntier = Cons (3, Cons (1 , Cons (2 , Nil))) ;; # longueur s1 ;; - : int = 3 inf201 : algorithmique et programmation fonctionnelle FP C.I) Définition récursive de séq(α) c) Généralisation : séquences de bool, séquence de réels, séquence de cartes, séquence de séquences … : seqEntier, seqBool, seqCarte, seqSeq ? => séq doit être un type polymorphe : séq(α) : séquence d'éléments du (même) type quelconque α (tous les éléments ont le même type, quelconque) Interêt : écriture de fonctions polymorphes sur les séquences (indépendantes du type des éléments de la séquence) En Caml, α est noté 'a, séq(α) est noté 'a list inf201 : algorithmique et programmation fonctionnelle FP Récursivité dans séq(α) C.II) Définition de fonctions récursives dans séq(α) a) Fonctions à 1 paramètre de type séq(α) Exemple : prédicat appartient SPEC Profil app : α → séq(α) → � Sémantique : (app e s) = vrai si e ϵ s Exemples : – (app 1 Nil) = faux – (app 1 Cons(1,Nil)) = vrai – (app 1 Cons(2,Cons(3,Nil))) = faux inf201 : algorithmique et programmation fonctionnelle FP Récursivité dans séq(α) REAL Équations de récurrence : (1) (app e Nil) = faux (2) (app e Cons(pr,fin)) = ? Tout dépend de e : (2a) (app e Cons(e,fin)) = vrai (2b) Soit e ≠pr, (app e Cons(pr,fin)) = (app e fin) inf201 : algorithmique et programmation fonctionnelle FP Récursivité dans séq(α) REAL Implémentation 1 : analyse par cas par filtrage et composition conditionnelle (pour discriminer les éq 2a et 2b) let rec app (e:'a) (s:'a seq) : bool = match s with | Nil -> false (* cf éq 1 *) | Cons(pr,fin) -> if e = pr then true (* cf éq 2a *) else (app e fin) (* cf éq 2b *) inf201 : algorithmique et programmation fonctionnelle FP Récursivité dans séq(α) REAL Implémentation 2 : analyse par cas par filtrage et composition booléenne let rec app (e:'a) (s:'a seq) : bool match s with | Nil -> false (* cf éq | Cons(pr,fin) -> e = pr || (app (* cf éq 2a et inf201 : algorithmique et programmation fonctionnelle = 1 *) e fin) 2b *) FP Récursivité dans séq(α) REAL Implémentation 3 : analyse par cas par composition conditionnelle et composition booléenne let rec app (e:'a) (s:'a seq) : bool = if s = Nil then false (* cf éq 1 *) else (* s non vide *) let Cons(pr,fin) = s in e = pr || (app e fin) (* cf éq 2a et 2b *) inf201 : algorithmique et programmation fonctionnelle FP Récursivité dans séq(α) REAL Implémentation 4 : analyse par cas par composition booléenne uniquement let rec app (e:'a) (s:'a seq) : bool = . . . Voir TD inf201 : algorithmique et programmation fonctionnelle FP C.II) Définition de fonctions récursives dans séq(α) b) Nouvelle notation (plus pratique) pour séq(α) ● ● Nil ≡> [] Cons(pr,fin) ≡> pr :: fin (::) : opérateur d’ajout à gauche) En Caml, 'a list est prédéfini grâce aux « constructeurs » [] et :: inf201 : algorithmique et programmation fonctionnelle FP C.II) Définition de fonctions récursives dans séq(α) Cons(1, Cons(2, Cons(3, Nil))) ≡> 1 :: (2 :: (3 :: []))) = 1 :: 2 :: 3 :: [] (:: est associatif à droite) démo (((1 :: 2) :: 3) :: []) => erreur de type (2 n’est pas une séq. d’entiers) 1 :: 2 :: 3 :: [] s’écrit plus simplement [1 ; 2 ; 3] inf201 : algorithmique et programmation fonctionnelle FP C.II) Définition de fonctions récursives dans séq(α) Le filtrage est possible sur des motifs utilisant [] et :: [] séquence vide [e] ou e::[] séquence à un seul élément (singleton) [e1; e2] ou e1::e2::[] e1::fin e1::e2::fin séquence à deux éléments séquence à au moins un élément (non vide) séquence à au moins deux éléments inf201 : algorithmique et programmation fonctionnelle FP C.II) Définition de fonctions récursives dans séq(α) c) Implémentation du prédicat app avec la nouvelle notation let rec app (e:'a ) (s:'a list) : bool = match s with | [] -> false | pr :: fin -> e = pr || (app e fin) app prédéfini en Caml : List.mem démo inf201 : algorithmique et programmation fonctionnelle FP C.II) Définition de fonctions récursives dans séq(α) Exemples d’appel app 1 [] ;; # - : bool = false app 1 [1 ; 2 ; 3] ;; # - : bool = true app 0 [1 ; 2 ; 3] ;; # - : bool = false app 'a' ['c' ; 'b' ; 'a'] ;; # - : bool = true # app 'a' [1 ; 2 ; 3] ;; Error: This expression has type int but an expression was expected of type char inf201 : algorithmique et programmation fonctionnelle FP C.II) Définition de fonctions récursives dans séq(α) d) Testeur et sélecteurs sur les séquences Testeur sur séq(α) (prédicat) : (_=[]) Profil (=[]) : séq(α) → � Sémantique : (=[]) (s) teste si s est la séquence vide Sélecteurs sur séq(α) : tete et queue Profils tete : séq(α)* → α queue: séq(α)* → séq(α) Rem : séq(α)* = séq(α) \ {[]} séquences non vide Sémantique : tete(s) : premier élément de la séquence s queue(s) : s privée de son premier élément inf201 : algorithmique et programmation fonctionnelle FP C.II) Définition de fonctions récursives dans séq(α) Attention : tête et queue non définies sur [] Propriété : ∀ e ϵ α, s ϵ séq(α), tete(e::s) = e queue(e::s) = s Réalisation de tête et queue : … /… tête et queue prédéfinies en Caml : List.hd (head) et List.tl (tail) inf201 : algorithmique et programmation fonctionnelle FP C.II) Définition de fonctions récursives dans séq(α) Implémentation d'une fonction séq(α) On choisira soit : - filtrage sur les motifs [] et pr :: fin - composition conditionnelle utilisant testeur et sélecteurs Exemple : nouvelle version du prédicat app let rec app (e:'a ) (s:'a list) : bool = if s=[] then false else (* s non vide *) e=(List.hd s) || (app e (List.tl s)) inf201 : algorithmique et programmation fonctionnelle FP Récursivité dans séq(α) C.III) Modèles d'analyse dans séq(α) a) Modèle d'analyse Mséq(α) (fonction quelconque récursive à un paramètre de type séq(α)): Composition - entité base Équations : [] (1) f [] = ... cas base - constructeur : (::) (2) f(pr::fin) = ... f(fin) ... cas réc. Décomposition 1 Implantation 1 : an. par cas par filtrage filtrage motifs [] _::_ let rec f (s:’a match s with | [] -> *) ... | pr::fin -> ... inf201 : algorithmique et programmation fonctionnelle list) : ... = (* d'après éq (1) : (* d'après éq (2) : *) f(fin)... FP C.III) Modèles d'analyse dans séq(α) Composition - entité base Équations : [] (1) f [] = ... cas base - constructeur : (::) Décomposition 2 (2) f(pr::fin) = ... f(fin) ... cas réc. Implantation 2 : an. par cas par exp. cond. testeur : et utilisation sélecteurs tête et queue _=[] sélecteurs : List.hd List.tl let rec f (s:’a list) : ... = if s =[] then (* d'après éq (1) : *) ... else (* d'après éq (2) : *) ... f(List.tl s)... inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) b) Application du modèle Mséq(α) SPÉC inversion d'une séquence Profil inv : séq(α) → séq(α) Sémant. inv ([e1 ; ... ; en]) = [en ; ... ; e1] Ex : a) b) c) inv ([2 ; 1 ; 3]) = [3 ; 1 ; 2] inv (['c' ; 'a' ; 'm' ; 'l' ]) = [‘l' ; ‘m' ; ‘a' ; ‘c' ]) inv ([‘k' ; 'a' ; ‘y' ; ‘a‘ ; ‘k’ ]) = [‘k' ; 'a' ; ‘y' ; ‘a‘ ; ‘k’ ] inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) Composition - entité base Équations : [] - constructeur : (::) (1) inv [] = ... cas base (2) inv (pr::fin) = ... inv (fin) ... cas réc. (1) Évident ([]) (2) prenons un exemple : pr::fin = 2::([1 ; 3]) inv(2::([1 : 3])) = ... inv ([1 ; 3]) ... [3 ; 1 ; 2] inf201 : algorithmique et programmation fonctionnelle [3 ; 1] Il suffit d'ajouter 2 à droite de [3 ; 1] FP C.III) Modèles d'analyse dans séq(α) Ajout de 2 à droite de [3 ; 1] => [3 ; 1] @ 2 [2] Opérateur @ : séq(α) → séq(α) → séq(α) prédéfini en Caml (concaténation de deux séquences d’éléments) RÉAL Algo inversion d'une séquence Équations : (1) inv [] = [] (2) inv (pr::fin) = inv (fin) @ [pr] → inf201 : algorithmique et programmation fonctionnelle Vérifier les types !! FP C.III) Modèles d'analyse dans séq(α) (2) inv (pr::fin) = inv (fin) @ [pr] RÉAL INFO inversion d'une séquence (suite) Terminaison : mesure(s) = |s| où |…| est la fonction cardinal 1) mesure est à valeur dans ℕ car |…| est de profil : séq(α) -> ℕ 2) mesure (pr :: fin) > mesure(fin) car |pr::fin| = 1 + |fin| et 1 + |fin| > |fin| inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) RÉAL INFO Décomposition filtrage motifs [] _::_ inversion d'une séquence (suite) Implantation : an. par cas par filtrage let rec inv (s:'a list) : match s with | [] -> (* d'après [] | pr::fin -> (* d'après inv(fin) @ inf201 : algorithmique et programmation fonctionnelle 'a list = éq (1) : *) éq (2) : *) [pr] FP C.III) Modèles d'analyse dans séq(α) RÉAL INFO inversion d'une séquence (suite) Décomposition 2 Implantation 2 : an. par cas par exp. cond. - testeur et utilisation sélecteurs tête et queue : _=[] - sélecteurs : List.hd (tête) List.tl (queue) let rec inv (s:'a list):'a list = if s = [] then (* d'après éq. (1) : *) [] else (* d'après éq. (2) : *) inv(List.tl s) @ [List.hd s] Nota : inv ([‘r’;’e’;’s’;’s’;’a’;’s’;’s’;’e’;’r’]) ? Une séquence p telle que inv(p)=p est dite “palindrome” inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) c) Fonctions à 1 paramètre de type séq(α)* Certaines fonctions ne sont définies que sur les séquences non vide, notées séq(α)* Exemple avec α = minuscule, séquence non vide de minuscules : mot DÉF MATH D'ENS déf minuscules = {'a', ..., 'z'} déf mot = séq(minuscule)* DÉF INFO DE TYPES type minuscule = char (* {'a', ..., 'z'} *) type mot = minuscule list (* non vide *) inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) SPÉC MATH longueur d'un mot Profil long : mot → ℕ* Sémant. long (m) est le nombre de lettres du mot m Ex : a) b) long (['o' ;'c' ;'a' ;'m' ;'l']) = 5 ∀c ∈ minuscule, long ([c]) = 1 inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) RÉAL INFO Algo longueur d'un mot Équations : (1) long ([pr]) = 1 (2) long (pr::fin) = 1 + long (fin) , où fin ≠ [] Terminaison mesure(...) = … preuve : .../... Implémentation .../... inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) d) Généralisation : modèle MSéq(α)* (* non vide *) singleton Composition Équations - entité base: [pr] (1) f ([pr]) = ... - constructeur : (::) Décomposition (2) f (pr::fin) = ... f (fin) ..., où fin ≠ [] Implantation : an. par cas par filtrage let rec f (s:'a list) : ... = match s with singleton | [e] -> (* d'après éq (1) : *) ... | pr::fin-> (* d'après éq (2) : *) ... f(fin)... NB : « this pattern-matching is not exhaustive » filtrage motifs [_] _::_ inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) e) Application de Mséq(α)* prédicat : une séquence est-elle croissante ? SPÉC MATH une séquence d'entiers est elle croissante ? Profil estCroiss : séq*(ℤ) →� Sémant. estCroiss ([e1 ;... ;en]) ssi e1 ≤ ... ≤ en (n ≥ 1) Ex : a) estCroiss ([1 ; 2 ; 2 ; 4]) = vrai b) estCroiss ([1 ; 3 ; 2]) = faux inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans Séq(α) Composition Équations - entité base: pr::[] (1) estCroiss ([pr]) = ... - constructeur : (::) (2) estCroiss (pr::fin) = ... estCroiss(fin) ... avec fin ≠ [] (1) vrai (2) prenons un exemple : pr::fin = 3::([1;2;3]) EstCroiss 3::([1;2;3]) = ... estCroiss ([1;2;3]) ... faux vrai 3 ≤ tête ([1;2;3]) et .. inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) RÉAL (suite) Équations : (1) estCroiss ([pr]) = vrai (2) estCroiss (pr::fin) = pr ≤ (tête fin) et estCroiss(fin) avec fin ≠ [] NB : «avec fin ≠ []» fondamental, car : tête : séq(α)* → a inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) RÉALISATION (fin) croissance d'une séquence d'entiers Implémentation let rec estCroiss (s:int list (* non vide *)): bool = match s with | [pr] -> true | pr :: fin -> (pr <= (List.hd fin)) && (estCroiss fin) inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) f) Fonctions à 1 paramètre séq(α) avec découpage à DROITE Jusqu'à présent : découpage à gauche d'une séquence s : s = pr::fin Certaines éq. réc. s'expriment naturellement selon un découpage à droite : s = déb @ ([der]) @ : séq(α) → séq(α) → séq(α) (concaténation) der : séq(α)* → α dernier élément de s déb : séq(α)* → séq(α) début de la séquence s s privé de der (analogue à queue) inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) exemple : valeurs cumulées d’une séquence SPEC Profil vc : séq(ℤ) → séq(ℤ ) Sémant. vc ([e1;e2;…;en]) = [e1;e1+e2;…;e1+e2+…en] Ex. & prop. vc([4;3;2]) = [4;7;9] ∀ e ∊ ℤ, vc([e]) = [e] inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) Réalisation Algo éq. rec. découpage à gauche ?? (1) vc([]) = [] (2) vc(pr::fin) = … vc(fin)… Exemple : vc(4::[3;2]) = f(vc([3;2]) = f([3;5]) ? Nécessite d’introduire une fonction f d’ajout d’un entier à tous les éléments d’une séquence d’entiers → Recherche d’une solution avec découpage à droite inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) Réalisation Algo éq. rec. découpage à droite (1) vc([]) = … (2) vc(deb@[der]) = … vc(deb)… Exemple : vc([4;3]@[2]) = f(vc([4;3]) = f([4;7]) ? Il suffit d’ajouter 9 à droite (somme de la séquence [4;3;2]) D’où : (1) vc([]) = [] (2) vc(deb@[der]) = vc(deb) @ [somme(deb@[der])] inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) Réalisation let rec vc (s: int list) : int list = match s with | [] -> [] | déb @ [der] -> vc(déb) @ [somme(déb@[der])] syntax error NB : filtrage non possible sur l’opérateur @ (ce n’est pas un constructeur) → analyse par cas par expression conditionnelle et utilisation des sélecteurs déb et der inf201 : algorithmique et programmation fonctionnelle FP C.III) Modèles d'analyse dans séq(α) g) Modèle d'analyse MDséq(α) modèle fonctions à 1 paramètre de type séq(α) avec découpage à droite Composition - entité base Équations : [] (1) f [] = ... cas base - constructeur : _@[_] (2) f(déb@[der]) = ... f(déb) ... cas réc. Décomposition Implantation : an. par cas par exp. cond. - testeur : _=[] let rec f (s:’a list) if s=[] then (* d'après éq else (* d'après éq ... f(déb - sélecteurs : déb der inf201 : algorithmique et programmation fonctionnelle : ... = (1) : *) ... (2) : *) s)... FP C.III) Modèles d'analyse dans séq(α) Implémentation de vc (fin) let rec vc (s: int list) : int list = if s=[] then [] else (vc (deb s)) @ [somme s] Avec somme, rec et der (application de MGséq(α)) let rec somme (s : int list) : int = match s with | [] -> 0 | pr::fin -> pr + somme(fin) let rec der (s : 'a list) : 'a = match s with | [e] -> e | pr::fin -> der(fin) let rec deb (s: 'a list):'a list = match s with | [e] -> [] | pr::fin -> pr::deb(fin) inf201 : algorithmique et programmation fonctionnelle FP