 
 
 
 Exercices
 Listes d'association
Voici un premier petit exercice simple dans lequel il vous est
demandé d'implanter un type abstrait polymorphe : les listes
d'association. Et d'en donner deux vues différentes.
- 
 Définir une signature ALIST 
déclarant un type (abstrait) paramétré par deux variables de
type (une pour la clé, l'autre pour la valeur stockée), une
fonction de création, une fonction d'ajout, une fonction d'accès,
un test d'appartenance et une fonction de suppression.
 
 # module type ALIST =
   sig
    type ('a,'b) t
    val create : unit -> ('a,'b) t
    val add : 'a -> 'b -> ('a,'b) t ->  ('a,'b) t
    val get : 'a -> ('a,'b) t ->  'b
    val mem : 'a -> ('a,'b) t ->  bool
    val rem : 'a -> ('a,'b) t ->  ('a,'b) t
   end ;;
 module type ALIST =
   sig
     type ('a, 'b) t
     val create : unit -> ('a, 'b) t
     val add : 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t
     val get : 'a -> ('a, 'b) t -> 'b
     val mem : 'a -> ('a, 'b) t -> bool
     val rem : 'a -> ('a, 'b) t -> ('a, 'b) t
   end
 
 On choisira une implantation fonctionnelle (ie : sans effet de
bord sur la donnée).
 
 
-  Définir le module Alist respectant la 
signature ALIST
 
 # module Alist:ALIST =
   struct
    type ('a,'b) t = ('a*'b) list
    let create () = []
    let add k v al = (k,v)::al
    let get = List.assoc
    let mem = List.mem_assoc
    let rem = List.remove_assoc
   end ;;
 module Alist : ALIST
 
 
 
 
-  Définir une signature ADM_ALIST 
pour l'administrateur des listes d'association. Il pourra uniquement
créer une liste, y rajouter et en retirer une entrée.
 
 # module type ADM_ALIST =
   sig
    type ('a,'b) t
    val create : unit -> ('a,'b) t
    val add : 'a  -> 'b -> ('a,'b) t -> ('a,'b) t
    val rem : 'a -> ('a,'b) t -> ('a,'b) t
   end ;;
 module type ADM_ALIST =
   sig
     type ('a, 'b) t
     val create : unit -> ('a, 'b) t
     val add : 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t
     val rem : 'a -> ('a, 'b) t -> ('a, 'b) t
   end
 
 
 
 
-  Définir une signature USER_ALIST 
pour les utilisateurs des listes qui n'auront droit qu'à l'accès
et au test d'appartenance.
 
 # module type USER_ALIST =
   sig
    type ('a,'b) t
    val get : 'a -> ('a,'b) t -> 'b
    val mem : 'a -> ('a,'b) t -> bool
   end ;;
 module type USER_ALIST =
   sig
     type ('a, 'b) t
     val get : 'a -> ('a, 'b) t -> 'b
     val mem : 'a -> ('a, 'b) t -> bool
   end
 
 
 
 
-  Créer deux modules AdmAlist et UserAlist pour les 
administrateurs et les utilisateurs.
Penser qu'un utilisateur doit pouvoir manipuler une liste
créée par un administrateur.
 
 # module AdmAlist = (Alist:ADM_ALIST with type ('a,'b) t = ('a,'b) Alist.t) ;;
 module AdmAlist :
   sig
     type ('a, 'b) t = ('a, 'b) Alist.t
     val create : unit -> ('a, 'b) t
     val add : 'a -> 'b -> ('a, 'b) t -> ('a, 'b) t
     val rem : 'a -> ('a, 'b) t -> ('a, 'b) t
   end
 # module UserAlist = (Alist:USER_ALIST with type ('a,'b) t = ('a,'b) Alist.t) ;;
 module UserAlist :
   sig
     type ('a, 'b) t = ('a, 'b) Alist.t
     val get : 'a -> ('a, 'b) t -> 'b
     val mem : 'a -> ('a, 'b) t -> bool
   end
 
 
 Vecteurs paramétrés
On illustre, dans ce deuxième exercice, comment les modules
paramétrés permettent la généricité et la réutilisation de
code en définissant un foncteur de manipulation de vecteurs que l'on
pourra instancier selon plusieurs types de nombres.
On se donne la signature suivante :
# module type NOMBRE =
  sig
   type a
   type t
   val create : a -> t
   val add : t -> t -> t
   val string_of : t -> string
  end ;;
- 
 Définir le foncteur FVecteur  
 paramétré par un module de signature
 NOMBRE qui définit un type t, une 
 fonction de création, l'addition (i.e. la translation) et la
 conversion en chaîne de caractères.
 
 # module  FVecteur (N:NOMBRE) =
   struct
    type e = N.t
    type t = { mutable vx:e; mutable vy:e }
    let create x0 y0 = { vx=x0; vy=y0 }
    let add v1 v2=  {vx = N.add v1.vx v2.vx; vy = N.add v1.vy v2.vy}
    let string_of v= "("^N.string_of v.vx^" , "^N.string_of v.vy^")"
   end ;;
 module FVecteur :
   functor(N : NOMBRE) ->
     sig
       type e = N.t
       and t = { mutable vx: e; mutable vy: e }
       val create : e -> e -> t
       val add : t -> t -> t
       val string_of : t -> string
     end
 
 
 
 
-  Définir une signature VECTEUR 
, non
 paramétrée, où les types des nombres et des vecteurs sont
 abstraits.
 
 # module type VECTEUR =
   sig
    type e
    type t
    val create : e -> e -> t
    val add :t -> t -> t
    val string_of : t -> string
   end ;;
 module type VECTEUR =
   sig
     type e
     and t
     val create : e -> e -> t
     val add : t -> t -> t
     val string_of : t -> string
   end
 
 
 
 
-  Définir trois structures Rationnel, 
Flottant et Complexe implantant la signature
NOMBRE. 
 
 # module Rationnel:NOMBRE =
   struct
    type a = int*int
    type t = { p:int; q:int }
    let create (p0,q0) = { p=p0; q=q0 }
    let add r1 r2 = { p = r1.p*r2.q + r2.p*r1.q; q = r1.q*r2.q }
    let string_of r = (string_of_int r.p)^"/"^(string_of_int r.q)
   end ;;
 module Rationnel : NOMBRE
 
 # module Flottant:NOMBRE =
   struct
    type a = float
    type t = a
    let create x = x
    let add = (+.)
    let string_of = string_of_float
   end ;;
 module Flottant : NOMBRE
 
 # module Complexe:NOMBRE =
   struct
    type a = float*float
    type t = { r:float; i:float }
    let create (r0, i0) = { r=r0; i=i0 }
    let add c1 c2 = { r = c1.r+.c2.r; i = c1.i+.c2.i }
    let string_of c = 
     (string_of_float c.r)^"+"^(string_of_float c.r)^"*i"
   end ;;
 module Complexe : NOMBRE
 
 
 
 
-  Utiliser ces structures pour définir, par application du foncteur
FVecteur, trois modules pour les 
 rationnels, les réels et les complexes. 
 
 # module RatVect:VECTEUR = FVecteur(Rationnel) ;;
 module RatVect : VECTEUR
 # module FloVect:VECTEUR = FVecteur(Flottant) ;;
 module FloVect : VECTEUR
 # module ComVect:VECTEUR = FVecteur(Complexe) ;;
 module ComVect : VECTEUR
 
 
 Arbres lexicaux
Cet exercice reprend les arbres lexicaux vus au chapitre
2, page ??, pour obtenir un module
générique de gestion des arbres lexicaux paramétrés par un
type abstrait pour les mots.
- 
 Définir la signature MOT contenant la 
déclaration d'un type abstrait alpha pour l'alphabet et
t pour les mots sur cet alphabet. On déclarera le mot vide,
l'opération de conversion d'un élément de l'alphabet en un mot,
l'accès à un élément d'un mot, l'extraction d'un sous-mot, la
longueur d'un mot et la concaténation de deux mots.
 
 # module type MOT =
   sig
    type alpha
    type t
    val null : t
    val of_alpha : alpha -> t
    val get : t -> int -> alpha
    val sub : t -> int -> int -> t
    val length : t -> int
    val concat : t -> t -> t
   end ;;
 module type MOT =
   sig
     type alpha
     and t
     val null : t
     val of_alpha : alpha -> t
     val get : t -> int -> alpha
     val sub : t -> int -> int -> t
     val length : t -> int
     val concat : t -> t -> t
   end
 
 
 
 
-  Définir le foncteur ArbLex, 
paramétré par un module de signature MOT, qui redéfinit
(en fonction des types et opérations sur les mots) le type des
arbres lexicaux et les fonctions existe, ajoute et
selecte de l'exercice du chapitre 2, page
??.
 
 # module ArbLex (M:MOT) =
    struct
      type node = Lettre of M.alpha * bool * t
      and t = node list
  
      let rec existe m d = 
        let  aux sm i n = 
          match d with
              [] -> false
            | (Lettre (a,b,l))::q when a = M.get sm i -> 
                if n = 1 then b else existe (M.sub sm (i+1) (n-1)) l       
            | (Lettre (a,b,l))::q when a = M.get sm i -> 
                existe sm q
      in aux m 0 (M.length m)
  
      let rec ajoute m d = 
        let aux sm i n = 
          if n = 0 then d 
          else 
            match d with 
                [] -> 
                  let aux = ajoute (M.sub sm (i+1) (n-1)) [] in
                  [ Lettre (M.get sm i, n = 1, aux) ] 
              | (Lettre(a,b,l))::q when a = M.get sm i ->
                  if n = 1 then (Lettre(a,true,l))::q
                  else Lettre(a,b,ajoute (M.sub sm (i+1) (n-1)) l)::q
              | (Lettre(a,b,l))::q -> (Lettre(a,b,l))::(ajoute sm q)
        in aux m 0 (M.length m)
  
      let rec selecte n d = match d with 
          [] -> []
        | (Lettre(a,b,l))::q when n=1 -> 
            let f (Lettre(a,b,_)) = if b then M.of_alpha a else M.null in 
            List.filter ((<>) M.null) (List.map f d) 
        | (Lettre(a,b,l))::q  -> 
            let r = selecte (n-1) l and r2 = selecte n q in
            let pr = List.map (function s -> M.concat (M.of_alpha a) s) r in
            pr@r2
    end ;;
 Characters 161-409:
 Warning: this pattern-matching is not exhaustive.
 Here is an example of a value that is not matched:
 _::_
 module ArbLex :
   functor(M : MOT) ->
     sig
       type node = | Lettre of M.alpha * bool * t
       and t = node list
       val existe : M.t -> t -> bool
       val ajoute : M.t -> t -> t
       val selecte : int -> t -> M.t list
     end
 
 
 
 
-  Définir le module Chars qui implante les opérations
de la signature MOT avec alpha = char et t = string. 
En déduire un module CharsDic  
pour les dictionnaires de chaînes de caractères.
 
 # module Chars =
    struct
      type alpha = char
      type t = string
      let null = ""
      let of_alpha c = String.make 1 c 
      let get s i = 
        try s.[i] 
        with Invalid_argument(_) -> raise (Invalid_argument "Chars.get")
      let sub s i1 i2 =
        try String.sub s i1 i2 
        with Invalid_argument(_) -> raise (Invalid_argument "Chars.sub")
      let length = String.length
      let concat = (^)
    end ;;
 module Chars :
   sig
     type alpha = char
     and t = string
     val null : string
     val of_alpha : char -> string
     val get : string -> int -> char
     val sub : string -> int -> int -> string
     val length : string -> int
     val concat : string -> string -> string
   end
 
 # module CharsDic = ArbLex(Chars) ;;
 module CharsDic :
   sig
     type node = ArbLex(Chars).node = | Lettre of Chars.alpha * bool * t
     and t = node list
     val existe : Chars.t -> t -> bool
     val ajoute : Chars.t -> t -> t
     val selecte : int -> t -> Chars.t list
   end
 
 
 
 
