Programmation fonctionnelle. Fonctions sur les listes. Notes de cours. Cours Septembre Sylvain Conchon

Documents pareils
# let rec concat l1 l2 = match l1 with [] -> l2 x::l 1 -> x::(concat l 1 l2);; val concat : a list -> a list -> a list = <fun>

Classes et Objets en Ocaml.

Recherche dans un tableau

Algorithmique et Programmation, IMA

Machines virtuelles fonctionnelles (suite) Compilation ML Java

length : A N add : Z Z Z (n 1, n 2 ) n 1 + n 2

IN Cours 1. 1 Informatique, calculateurs. 2 Un premier programme en C

Cours d Algorithmique-Programmation 2 e partie (IAP2): programmation 24 octobre 2007impérative 1 / 44 et. structures de données simples

TP3 : Manipulation et implantation de systèmes de fichiers 1

introduction Chapitre 5 Récursivité Exemples mathématiques Fonction factorielle ø est un arbre (vide) Images récursives

modules & compilation

Cours de Programmation 2

TP 1. Prise en main du langage Python

Présentation du langage et premières fonctions

Utilisation d objets : String et ArrayList

Logiciel Libre Cours 3 Fondements: Génie Logiciel

1/24. I passer d un problème exprimé en français à la réalisation d un. I expressions arithmétiques. I structures de contrôle (tests, boucles)

Cours 1 : La compilation

STAGE IREM 0- Premiers pas en Python

1. Structure d un programme C. 2. Commentaire: /*..texte */ On utilise aussi le commentaire du C++ qui est valable pour C: 3.

Cours intensif Java. 1er cours: de C à Java. Enrica DUCHI LIAFA, Paris 7. Septembre Enrica.Duchi@liafa.jussieu.fr

Découverte de Python

INITIATION AU LANGAGE C SUR PIC DE MICROSHIP

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

Les chaînes de caractères

Java Licence Professionnelle CISII,

PHP et mysql. Code: php_mysql. Olivier Clavel - Daniel K. Schneider - Patrick Jermann - Vivian Synteta Version: 0.9 (modifié le 13/3/01 par VS)

Chapitre 10. Les interfaces Comparable et Comparator 1


Prénom : Matricule : Sigle et titre du cours Groupe Trimestre INF1101 Algorithmes et structures de données Tous H2004. Loc Jeudi 29/4/2004

Initiation à la programmation en Python

Les arbres binaires de recherche

Structure d un programme et Compilation Notions de classe et d objet Syntaxe

Algorithmique et programmation : les bases (VBA) Corrigé

Rappel. Analyse de Données Structurées - Cours 12. Un langage avec des déclaration locales. Exemple d'un programme

Licence Bio Informatique Année Premiers pas. Exercice 1 Hello World parce qu il faut bien commencer par quelque chose...

Expression des contraintes. OCL : Object C o n t r a i n t L a n g u a g e

KL5121. Pour activer des sorties en fonction de la position d'un codeur

MISE A NIVEAU INFORMATIQUE LANGAGE C - EXEMPLES DE PROGRAMMES. Université Paris Dauphine IUP Génie Mathématique et Informatique 2 ème année

Initiation à la Programmation en Logique avec SISCtus Prolog

Cours de C++ François Laroussinie. 2 novembre Dept. d Informatique, ENS de Cachan

Conventions d écriture et outils de mise au point

Bases de programmation. Cours 5. Structurer les données

Programme Compte bancaire (code)

Une introduction rapide à Coq

TP, première séquence d exercices.

Vérification de programmes et de preuves Première partie. décrire des algorithmes

EPREUVE OPTIONNELLE d INFORMATIQUE CORRIGE

1 Recherche en table par balayage

Notions fondamentales du langage C# Version 1.0

Chap III : Les tableaux

Une dérivation du paradigme de réécriture de multiensembles pour l'architecture de processeur graphique GPU

LMI 2. Programmation Orientée Objet POO - Cours 9. Said Jabbour. jabbour@cril.univ-artois.fr

Cours d introduction à l informatique. Partie 2 : Comment écrire un algorithme? Qu est-ce qu une variable? Expressions et instructions

Probabilités. Rappel : trois exemples. Exemple 2 : On dispose d un dé truqué. On sait que : p(1) = p(2) =1/6 ; p(3) = 1/3 p(4) = p(5) =1/12

Suivant les langages de programmation, modules plus avancés : modules imbriqués modules paramétrés par des modules (foncteurs)

PROJET ALGORITHMIQUE ET PROGRAMMATION II

Résolution de systèmes linéaires par des méthodes directes

Exercices types Algorithmique et simulation numérique Oral Mathématiques et algorithmique Banque PT

Projet d informatique M1BI : Compression et décompression de texte. 1 Généralités sur la compression/décompression de texte

TD3: tableaux avancées, première classe et chaînes

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile

I. Introduction aux fonctions : les fonctions standards

ARBRES BINAIRES DE RECHERCHE

Travaux pratiques. Compression en codage de Huffman Organisation d un projet de programmation

Introduction à la programmation Travaux pratiques: séance d introduction INFO0201-1

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java

Examen Médian - 1 heure 30

Introduction à JDBC. Accès aux bases de données en Java

Architecture des Systèmes d Information Architecture des Systèmes d Information

CHAPITRE 9. Codes source. 9.1 Insertion brute

Premiers Pas en Programmation Objet : les Classes et les Objets

Algorithmique, Structures de données et langage C

Solutions du chapitre 4

Module Administration BD Chapitre 1 : Surcouche procédurale dans les SGBDS

Anne Tasso. Java. Le livre de. premier langage. 10 e édition. Avec 109 exercices corrigés. Groupe Eyrolles, , ISBN :

Algorithmes récursifs

1 Lecture de fichiers

OCL - Object Constraint Language

Licence Sciences et Technologies Examen janvier 2010

Programmation en Caml pour Débutants

Quelques algorithmes simples dont l analyse n est pas si simple

Cours 1 : Introduction. Langages objets. but du module. contrôle des connaissances. Pourquoi Java? présentation du module. Présentation de Java

Correction Code nécessaire à la compilation : let bs ="\\" let nl = "\n" ;; let appliquer = List.map ;; (* affichage d'un noeud *)

Package Java.util Classe générique

Utilitaires méconnus de StrataFrame

Programmation VBA/Excel. Programmation VBA. Pierre BONNET. Masters SMaRT & GSI - Supervision Industrielle P. Bonnet

Travaux Dirigés n 1 : chaînes de caractères

Quelques Algorithmes simples

Flux de données Lecture/Ecriture Fichiers

Programmation C++ (débutant)/instructions for, while et do...while

Programmation en Java IUT GEII (MC-II1) 1

Introduction au langage C

Éléments d informatique Cours 3 La programmation structurée en langage C L instruction de contrôle if

1 de 46. Algorithmique. Trouver et Trier. Florent Hivert. Mél : Florent.Hivert@lri.fr Page personnelle : hivert

SHERLOCK 7. Version du 01/09/09 JAVASCRIPT 1.5

Java Licence Professionnelle Cours 7 : Classes et méthodes abstraites

2 Comment fonctionne un ordinateur, dans les grandes lignes

Initiation à l algorithmique

Transcription:

Programmation fonctionnelle avancée Notes de cours Cours 3 Fonctions sur les listes 19 Septembre 2018 Sylvain Conchon sylvain.conchon@lri.fr 1/43 12/43 2 Définitions de fonctions sur les listes La définition des fonctions sur les listes prennent généralement la forme d une définition à deux cas : le cas où liste est vide le cas où elle ne l est pas Pour cette raison, il est plus agréable de réaliser cette analyse par cas avec du filtrage : let rec f l = [] ->... x::s ->... La fonction zeros La fonction zeros vérifie que tous les éléments d une liste d entiers sont des 0 (renvoie true si la liste est vide) let rec zeros l = [] -> true x::s -> x=0 && zeros s Cette fonction a pour type int list -> bool # zeros [0;0;0];; - : bool = true # zeros [0;1;0];; - : bool = false # zeros [];; - : bool = true 3/43 34/43 4

Recherche d un entier dans une liste La fonction recherche détermine si un entier n figure bien dans une liste l let rec recherche n l = [] -> false x::s -> x=n recherche n s Longueur d une liste La fonction longueur retourne la longueur d une liste let rec longueur l = [] -> 0 _::s -> 1 + (longueur s) Une version récursive terminale : Cette fonction a pour type int list -> bool # recherche 4 [3;2;4;1];; - : bool = true # recherche 4 [1;2];; - : bool = false let longueur l = let rec longrec acc l = [] -> acc _::s -> longrec (1+acc) s in longrec 0 l Cette fonction est prédéfinie en OCAML : List.length 5/43 56/43 6 Fonctions polymorphes (1/2) Quel est le type de la fonction longueur? longueur doit pouvoir s appliquer à des listes d entiers, comme par exemple Polymorphisme # longueur [4;3;6;1;10];; - : int = 5... alors elle doit avoir le type suivant val longueur : int list -> int Mais cette fonction doit aussi pouvoir être appliquée sur une liste dont les éléments sont d un autre type, comme par exemple : # longueur [[4.5;0.3;9.8];[];[3.2;1.8]];; - : int = 3... dans ce cas la fonction longueur devrait également avoir 7/43 78/43 8

Fonctions polymorphes (2/2) Les deux types précédents sont corrects La fonction longueur a une infinité de types Le type inféré par OCAML est le plus général : val longueur : a list -> int a (qui se lit apostrophe a, ou encore alpha ) est une variable de type Une variable de type veut dire n importe quel type Il faut donc lire le type de la fonction longueur comme suit : La fonction longueur prend en argument une liste dont les éléments sont de n importe quel type et retourne un entier Types sommes polymorphes Les type sommes peuvent aussi être polymorphes type a option = None Some of a # None ;; - : a option = None # let v = Some(3);; val v : int option = Some(3) Un autre type somme polymorphe bien connu. type a liste = Vide Cellule of a * a liste # Vide ;; - : a liste = Vide # let l = Cellule(3,Vide);; val l : int liste = Cellule(3,Vide) 9/43 910/43 10 Types sommes polymorphes Les types peuvent également contenir plusieurs variables de type type ( a, b) t = A of a * b B of int Fonctions génériques sur les listes # A(1.2, "r") ;; - : (float, string) t = A(1.2, "r") # A( a, 1) ;; - : (char, int) t = A( a, 1) 11/43 11 12/43 12

Concaténation de listes La fonction append construit une nouvelle la liste en réunissant deux listes bout à bout : let rec append l1 l2 = match l1 with [] -> l2 x::s -> x::(append s l2) Cette fonction a pour type a list -> a list -> a list # append [2;5;1] [10;6;8;15];; - : int list = [2;5;1;10;6;8;15] Cette fonction est prédéfinie en OCAML, il s agit de List.append L opérateur infixe @ est un raccourci syntaxique pour cette fonction, on note l1@l2 la concaténation de l1 et l2 Concaténation rapide La fonction append n est pas récursive terminale Si l ordre des éléments n a pas d importance, on peut définir une concaténation récursive terminale qui inverse les éléments de la première liste let rec rev_append l1 l2 = match l1 with [] -> l2 x :: s -> rev_append s (x :: l2) val rev_append : a list -> a list -> a list = <fun> # rev_append [4;2;6] [1;10;9;5];; - : int list = [6; 2; 4; 1; 10; 9; 5] 13/43 13 14/43 14 Renverser une liste La fonction rev pour renverser une liste l s obtient facilement en concaténant la liste l avec la liste vide [], en utilisant rev append let rev l = rev_append l [] Tri de listes val rev : a list -> a list = <fun> # rev [4;2;6;1];; - : int list = [1; 6; 2; 4] 15/43 15 16/43 16

Tri pas Insertion : principe Cet algorithme de tri suit de manière naturelle la structure récursive des listes Soit l une liste à trier : 1. si l est vide alors elle est déjà triée 2. sinon, l est de la forme x::s et, on trie récursivement la suite s et on obtient une liste triée s on insert x au bon endroit dans s et on obtient une liste triée Insertion La fonction inserer permet d insérer un élément x dans une liste l Si la liste l est triée alors x est inséré au bon endroit On prend pour le moment <= comme relation d ordre let rec inserer x l = [] -> [x] y::s -> if x<=y then x::l else y::(inserer x s) val inserer: a -> a list -> a list # inserer 5 [3;7;10];; - : int list = [3; 5; 7; 10] 17/43 17 18/43 18 Trier une liste Tri Rapide : principe On utilise la fonction inserer pour réaliser un tri par insertion d une liste let rec trier l = [] -> [] x::s -> inserer x (trier s) val trier : a list -> a list = <fun> # trier [6; 1; 9; 4; 3];; - : int list = [1; 3; 4; 6; 9] Soit une liste l à trier : 1. si l est vide alors elle est triée 2. sinon, choisir un élément p de la liste (le premier par exemple) nommé le pivot 3. partager l en deux listes g et d contenant les autres éléments de l qui sont plus petits (resp. plus grands) que la valeur du pivot p 4. trier récursivement g et d, on obtient deux listes g et d triées 5. on renvoie la liste g @[p]@d (qui est bien triée) 19/43 19 20/43 20

Partage d une liste La fonction suivante permet de partager une liste l en deux sous-listes g et d contenant les éléments de l plus petits (resp. plus grands) qu une valeur donnée p let rec partage p l = [] -> ([], []) x::s -> let (g, d) = partage p s in if x <= p then (x::g, d) else (g, x::d) val partage : a -> a list -> a list * a list = <fun> # partage 5 [1;9;7;3;2;4];; - : int list * int list = ([1; 3; 2; 4], [9; 7]) Tri rapide let rec tri_rapide l = [] -> [] p::s -> let (g, d) = partage p s in (tri_rapide g)@[p]@(tri_rapide d) val tri_rapide : a list -> a list = <fun> # tri_rapide [5; 1; 9; 7; 3; 2; 4];; - : int list = [1; 2; 3; 4; 5; 7; 9] 21/43 21 22/43 22 Schémas de définitions récursives Les fonctions suivantes ont toutes la même structure : Itérateurs sur les listes la fonction zeros la fonction recherche la fonction longueur la fonction append la fonction existe la fonction map etc. Laquelle? 23/43 23 24/43 24

Schéma récursif en commun Itération d ordre supérieur Ces fonctions ont toutes le schéma récursif suivant (on note l la liste en entrée et f la fonction définie récursivement) : 1. si l est la liste vide, la valeur retournée par f ne dépend pas de l : c est le cas de base de la récursion ; 2. sinon, l est de la forme x::s et la valeur retournée est calculée en effectuant une opération à partir de x et f s On peut capturer ce schéma à l aide d une fonction d ordre supérieur prenant en argument une fonction f (à deux arguments), une liste l et un élément de départ acc L argument acc représente la valeur retournée pour le cas de base de la récursion La fonction f est appliquée à chaque élément de la liste ainsi qu au résultat de l appel récursif 25/43 25 26/43 26 Exemple : la fonction somme Étape 1 : extraire l opération récursive On montre comment abstraire le schéma d une définition récursive à partir de la fonction somme suivante : let rec somme l = [] -> 0 x::s -> x + (somme s) let rec somme l = [] -> 0 x::s -> (fun a b -> a + b) x (somme s) 27/43 27 28/43 28

Étape 2 : abstraire l opération récursive Étape 4 : abstraire l accumulateur let rec somme fold f l = [] -> 0 x::s -> f x (somme fold f s) let somme l = somme fold (fun a b -> a + b) l let rec somme fold f l acc = [] -> acc x::s -> f x (somme fold f s acc) let somme l = somme fold (fun a b -> a + b) l 0 ou plus simplement let somme l = somme fold (+) l 0 29/43 29 30/43 30 Déroulons tout ça somme [1;2;3] somme fold (+) [1;2;3] 0 (+) 1 (somme fold (+) [2;3] 0) (+) 1 ((+) 2 (somme fold (+) [3] 0)) (+) 1 ((+) 2 ((+) 3 (somme fold (+) [] 0))) (+) 1 ((+) 2 ((+) 3 0)) (+) 1 ((+) 2 3) (+) 1 5 6 L itérateur fold right La fonction somme fold n est pas spécifique au calcul de la somme d une liste d entiers. Son schéma récursif est capturé par la fonction suivante : fold right f [ ] acc = acc fold right f [ e 1 ; e 2 ;... ; e n ] acc = f e 1 (f e 2 ( (f e n acc) )) Cette fonction s appelle List.fold right dans la bibliothèque standard de OCaml : let rec fold_right f l acc = [] -> acc x::l -> f x (fold_right f l acc) Son type est ( a -> b -> b) -> a list -> b -> b Attention : cette fonction n est pas récursive terminale! 31/43 31 32/43 32

L itérateur récursif terminal fold left Pour les fonctions dont l ordre d application de l opération sur x et g s n est pas important, on peut utiliser le parcours suivant : fold left f acc [ ] = acc fold left f acc [ e 1 ; e 2 ;... ; e n ] = f ( (f (f acc e 1 ) e 2 ) ) e n Exemple 1 : somme d une liste d entiers (suite) On peut écrire la fonction somme avec List.fold left let somme l = fold_left (+) 0 l val somme : int list -> int Cette fonction s appelle List.fold left dans la bibliothèque standard de OCaml : let rec fold_left f acc l = [] -> acc x::s -> fold_left f (f acc x) s Son type est ( a -> b -> a) -> a -> b list -> a Cette fonction est récursive terminale! somme [1;2;3] = fold left (+) 0 [1;2;3] fold left (+) ((+) 0 1) [2;3] = fold left (+) 1 [2;3] fold left (+) ((+) 1 2) [3] = fold left (+) 3 [3] fold left (+) ((+) 3 3) [] = fold left (+) 6 [] 6 33/43 33 34/43 34 Exemple 2 : Longueur d une liste Évaluation de la fonction longueur Rappel : version sans itérateur let rec longueur l = [] -> 0 x::s -> 1 + (longueur s) val longueur : a list -> int Version avec itérateur : let longueur l = fold_left (fun acc x -> 1 + acc) 0 l longueur : a list -> int # longueur [3;2;1;4];; - : int = 4 Évaluation de longueur [3;2;1;4] On note plus1 la fonction (fun acc x -> 1 + acc) longueur [3;2;1;4] = fold left plus1 0 [3; 2; 1; 4] fold left plus1 (plus1 0 3) [2; 1; 4] = fold left plus1 1 [2; 1; 4] fold left plus1 (plus1 1 2) [1; 4] = fold left plus1 2 [1; 4] fold left plus1 (plus1 2 1) [1; 4] = fold left plus1 3 [4] fold left plus1 (plus1 3 4) [] = fold left plus1 4 [] = 4 35/43 35 36/43 36

Exemple 3 : concaténation de deux listes Rappel : version sans itérateur let rec append l1 l2 = match l1 with [] -> l2 x::s -> x::(append s l2) append : a list -> a list -> a list Version avec itérateur let append l1 l2 = fold_right (fun x acc -> x::acc) l1 l2 val append : a list -> a list -> a list = <fun> # append [1;2;3] [4;5;6];; - : int list = [1; 2; 3; 4; 5; 6] Autres fonctions let zeros = fold_left (fun acc x -> x=0 && acc) true val zeros : int list -> bool = <fun> let recherche n = fold_left (fun acc x -> x=n acc) false val recherche : a -> a list -> bool = <fun> let existe p = fold_left (fun acc x -> p x acc) false val existe : ( a -> bool) -> a list -> bool = <fun> 37/43 37 38/43 38 Exemple 4 : liste des sous-listes d une liste (1/2) Exemple 4 : liste des sous-listes d une liste (2/2) On souhaite écrire une fonction sous listes pour calculer la liste des sous-listes d une liste l On commence par écrire une fonction cons qui ajoute un élément à toutes les listes d une liste de listes : let rec cons_elt x l = [] -> [] r::s -> (x::r)::(cons_elt x s) cons_elt : a -> a list list -> a list list La fonction sous liste s écrit alors naturellement de la manière suivante : let rec sous_listes l = [] -> [[]] x::s -> let p = sous_listes s in (cons_elt x p)@p sous_listes : a list -> a list list # sous_listes [1;2;3];; - : int list list = [[1; 2; 3]; [1; 2]; [1; 3]; [1]; [2; 3]; [2]; [3]; []] 39/43 39 40/43 40

Exemple 5 : sous liste avec itérateurs On commence par définir l itérateur map de type ( a -> b) -> a list -> b list tel que map f [e 1 ;...; e n ] = [f e 1 ;...; f e n ] let rec map f l = [] -> [] x::s -> let v = f x in v :: (map f s) Itérateurs vs. fonctions récursives (1/2) Les versions sans itérateurs des fonctions zeros, recherche et existe sont plus efficaces que leurs versions avec itérateurs respectives let rec existe p l = [] -> false x::s -> p x (existe p s) On note p la fonction fun x -> x=0 Le fonction sous liste peut alors s écrire (avec @ l opérateur de concaténation en OCaml) : let sous_listes = fold_left (fun p x -> (map (fun l->x::l) p)@p) [[]] existe p [3;0;2;1;4] p 3 existe p [0;2;1;4] = existe p [0;2;1;4] p 0 existe p [2;1;4] = true 41/43 41 42/43 42 Itérateurs vs. fonctions récursives (2/2) La version avec itérateur va jusqu au bout de la liste let f = fun acc x -> p x acc let existe p = fold_left f false existe p [3;0;2;1;4] = fold left f false [0;2;1;4] fold left f (p 3 false) [0;2;1;4] = fold left f false [0;2;1;4] fold left f (p 0 false) [2;1;4] = fold left f true [0;2;1;4] fold left f (p 2 true) [1;4] = fold left f true [1;4] fold left f (p 1 true) [4] = fold left f true [4] fold left f (p 4 true) [] = fold left f true [] 43/43 43