LI213 Types et Structures de données Licence d Informatique Université Paris 6
Définition Pile Une pile est une structure de données représentant un ensemble de données unitaires (homogènes), permettant l insertion aisée de nouvelles données permettant l extraction aisée de la dernière donnée insérée. Insertion Suppression structure en pile d assiettes Appelée LIFO : Last In, First Out Fréquemment utilisée en informatique : Évaluation d expression arithmétique Pile d exécution (pile d appels des fonctions) Undo/Redo dans un logiciel
Opérations usuelles sur les piles Quelles opérations a-t-on envie d implanter sur une pile? Opérations usuelles Créer une pile vide : creepile Empiler un élément dans la pile : empile P e Dépiler un élément dans la pile : depile P Tester si une pile est vide : estpilevide P Récupérer l élément en tête de pile : tetepile P tetepile(empile P e)=e depile(empile P e)=p En tout cas : type polymorphe + effet de bord partout!
1 : liste référencée Une pile peut être représentée par une liste, modifiable par effet de bord : une liste référencée. # type a pile = a list ref;; On redéfinit un type de base : on devra utiliser ce type explicitement! # let creepile () = (ref [] : a pile);; # let empile (p : a pile) e = p := e ::!p;; # let depile (p : a pile) = match!p with [] -> failwith "pile vide" x :: y -> p := y;; # let tetepile (p : a pile) = match!p with [] -> failwith "pile vide" x :: y -> x;; # let estpilevide (p : a pile) = match!p with [] -> true -> false;;
Nota Bene : Type faible # let creepile () = (ref [] : a pile);; val creepile : unit -> a pile = <fun> # let l = creepile();; val l : a pile = {contents = []} Type faible Problème : référence à des types polymorphes (paramétrés) # let l= ref [];; # l := 4 ::!l;; (* comme si l contenait une int list *) # l := "youpi" ::!l;; (* et aussi une string list *) Solution : indiquer qu un type polymorphe référencé n est plus polymorphe, mais un type inconnu, en attente d instanciation : type faible qui s écrit a. let h=[] ;; let l = ref [] ;; l :=4::!l ; l ;; a list a list int list
2 : enregistrement à champs list mutable Un enregistrement permet d éviter de forcer le typage et de rajouter des informations (comme la taille de la pile n) # type a pile = {mutable p : a list ; mutable n :int};; # let creepile () = {p=[] ; n=0};; # let empile pile e = pile.p <- e :: pile.p ; pile.n <- pile.n+1;; # let depile pile = match pile.p with [] -> failwith "pile vide" x :: y -> pile.p <- y ; pile.n <- pile.n-1;; # let tetepile pile = match pile.p with [] -> failwith "pile vide" x :: y -> x ;; # let estpilevide pile = pile.n=0;;
3 : pile de taille finie Si on peut connaître la taille max. de la pile : utilisation de tableau. 0 n lg-1 # type a pile = {p : a array ; mutable n :int ;lg :int};; # let creepile nbr x = { p = Array.make nbr x ; lg = nbr ; n = 0 } ;; # let empile pile e = if (pile.n=pile.lg) then failwith "pile pleine" else ( pile.p.(pile.n)<-e ; pile.n<-pile.n+1 ) ;; # let depile pile = if (pile.n=0) then failwith "vide" else pile.n<-pile.n-1 ;; # let tetepile pile = if (pile.n=0) then failwith "vide" else pile.p.(pile.n-1) ;; # let estpilevide pile = pile.n=0;;
4 : module Stack Module Stack # Stack.create;; (* = creepile *) - : unit -> a Stack.t = <fun> # Stack.push;; (* = empile *) - : a -> a Stack.t -> unit = <fun> # Stack.pop;; (* = tetepile+depile *) - : a Stack.t -> a = <fun> Comment écrire pop? # let pop pile = let e = tetepile pile in depile pile ;e;;
Récursivité = séquentiel + Pile Fibo toujours... simulation de récursivité # let fibo n = let p = CreePile () and res=ref 0 in empile p n ; while not (estpilevide p) do let e=tetepile p in ( depile P ; match e with 0 1 -> res :=1+!res -> begin empile p (e-1) ; empile p (e-2) end ) done ;!res;;
Récursivité et pile On suppose une pile qu on veut garder ordonnée. Tri par insertion avec pile # let rec ranger pile x = if estvidepile pile then empiler pile x else if (x > tetepile pile) then empiler pile x else let y=tetepile pile in ( depiler pile ; ranger pile x ; empiler pile y ) ;;
Définition File Une file est une structure de données représentant un ensemble de données unitaires (homogènes), permettant l insertion aisée de nouvelles données permettant l extraction aisée de la plus ancienne donnée insérée. Insertion Suppression Appelée FIFO : First In, First Out Fréquemment utilisée en informatique : file d attente pipe linux
Opérations usuelles sur les files Quelles opérations a-t-on envie d implanter sur une file? Opérations usuelles Créer une file vide : creefile Insérer un élément dans la file : enfile F e Supprimer un élément dans la file : defile F Tester si une file est vide : estfilevide F Récupérer l élément en queue de file : queuefile F queuefile(enfile F e)= si F= alors e sinon queuefile F defile(enfile F e)= si F= alors sinon enfile (defile F) e En tout cas : type polymorphe + effet de bord partout!
1 : liste mutable On évite l implantation en liste référencée et on utilise tout de suite un enregistrement. # type a file = {mutable f : a list};; # let creefile () = {f=[]};; # let enfile file e = file.f <- file.f @ [e];; # let defile file = match file.f with [] -> failwith "file vide" x :: y -> file.f <- y ;; # let queuefile file = match file.f with [] -> failwith "file vide" x :: y -> x ;; # let estfilevide pile = file.f=[];;
2 : file de taille finie Si on connaît la taille max. de la file : utilisation circulaire d un tableau 0 debut fin lg-1 0 fin debut lg-1 # type a file = {f : a array ; lg :int ; mutable debut :int ; mutable fin :int};; # let creefile nbr x = { f = Array.make nbr x ; lg = nbr ; debut = 0 ; fin = 0 } ;; # let enfile file e = let pos = (file.fin+1) mod file.lg in if (pos=file.debut) then failwith "File pleine" else ( file.p.(file.fin)<-e ; file.fin<-pos ) ;;
2bis : file de taille finie Si on connaît la taille max. de la file : utilisation circulaire d un tableau. 0 debut fin lg-1 0 fin debut lg-1 # let defile file = if (file.debut=file.fin) then failwith "File vide" else file.debut<-(file.debut+1) mod file.lg ;; # let queuefile file = if (file.debut=file.fin) then failwith "File vide" else file.f.(file.debut) ;; # let estfilevide pile = file.debut=file.fin;;
3 : module Queue Module Queue # Queue.create;; (* = creefile *) - : unit -> a Queue.t = <fun> # Queue.add;; (* = enfile *) - : a -> a Queue.t -> unit = <fun> # Queue.take;; (* = queuefile+defile *) - : a Queue.t -> a = <fun> Comment écrire take? # let take file = let e = queuefile file in defile file ;e;;
Encapsulation Pile et file en OCAML utilise des objets mutables (référence, enregistrement, tableau, etc.). Ce qui rend la programmation peu sûre : # let l = creepile () ;;; val t : a pile = {p = [] ; n=0} # t.p <- 1 :: t.p;;!!! Encapsulation # type a superpile = { empile : a -> unit ; depile : unit -> unit ; estpilevide : unit -> bool tetepile : unit -> a } ;;
Encapsulation - 2 Construction de superpile # let newsuperpile () = let pile = ref[] and n=ref 0 in { empile = (function x -> pile :=x ::!pile ; n <- 1+!n) ; depile = (function () -> if!n = 0 then...) ; estpilevide = (function () ->!n=0) ; tetepile = (function () -> if!n=0 then...) } ;; # let pile = newsuperpile();; pile : a t =... # pile.empile 4;; # pile.empile 25;; # pile.tetepile;; - : int = 25