Précédent Index Suivant

Exercices

Piles en objet

On reprend l'exemple des piles vu précédemment à la sauce objet.
  1. Définir une classe intpile en utilisant les listes de O'Caml implantant des listes d'entier et disposant des méthodes empile, depile, sommet, et taille.
    (* 1.1 *)
    exception PileVide
    class intpile () = 
      object 
        val p = ref ([] : int list)
        method empile i = p := i:: !p
        method depile () = 
          if !p = [] then raise PileVide
          else p := List.tl !p
        method sommet () = 
          if !p = [] then raise PileVide
          else  List.hd !p
        method taille () = 
          List.length !p
      end;;
    
  2. Créer une instance contenant 3 et 4 comme élément de la pile.
    (* 1.2 *)
    let p = new intpile ();;
    p#empile 3;;
    p#empile 4;;
    
  3. Définir une nouvelle classe pile contenant des éléments répondant à la méthode print : unit -> unit.
    (* 1.3 *)
    class pile () = 
      object 
        val p = ref ([] : <print : unit -> unit>  list)
        method empile i = p := i:: !p
        method depile () = 
          if !p = [] then raise PileVide
          else p := List.tl !p
        method sommet () = 
          if !p = [] then raise PileVide
          else  List.hd !p
        method taille () = 
          List.length !p
      end;;
    
  4. Définir une classe paramétrée ['a] pile avec les mêmes méthodes.
    (* 1.4 *)
    class ['a] ppile () = 
      object 
        val p = ref ([] : 'a  list)
        method empile i = p := i:: !p
        method depile () = 
          if !p = [] then raise PileVide
          else p := List.tl !p
        method sommet () = 
          if !p = [] then raise PileVide
          else  (List.hd !p) 
        method taille () = 
          List.length !p
      end;;
    
  5. Comparer les différentes classes de piles.

Liaison retardée

Cet exercice illustre la notion de liaison retardée indépendamment de la notion de sous-typage.

Soit le programme donné par la suite :
  1. Dessiner les relations entre classes
  2. Tracer les différents envois de message.
  3. En supposant être dans un mode caractère sans écho, indiquer ce qu'affiche le programme.

exception CrLf;;
class lecture_chaine (m) =
object (self)
val msg = m
val mutable res = ""

method lire_char =
let c = input_char stdin in
if (c != '\n') then begin
output_char stdout c; flush stdout
end;
String.make 1 c

method private lire_chaine_aux =
while true do
let s = self#lire_char in
if s = "\n" then raise CrLf
else res <- res ^ s;
done

method private lire_chaine_aux2 =
let s = self#lire_char in
if s = "\n" then raise CrLf
else begin res <- res ^ s; self#lire_chaine_aux2 end

method lire_chaine =
try
self#lire_chaine_aux
with End_of_file -> ()
| CrLf -> ()

method input = res <- ""; print_string msg; flush stdout;
self#lire_chaine

method get = res
end;;

class lecture_mdp (m) =
object (self)
inherit lecture_chaine m
method lire_char = let c = input_char stdin in
if (c != '\n') then begin
output_char stdout '*'; flush stdout
end;

let s = " " in s.[0] <- c; s
end;;

let login = new lecture_chaine("Login : ");;
let passwd = new lecture_mdp("Passwd : ");;
login#input;;
passwd#input;;
print_string (login#get);;print_newline();;
print_string (passwd#get);;print_newline();;


Classes abstraites et évaluateur d'expressions

Cet exercice a pour but de montrer la factorisation de code réalisé par l'utilisation de classes abstraites.

Les expressions arithmétiques construites sont toutes des instances d'une sous-classe de la classe abstraite expr_ar.
  1. Définir une classe abstraite expr_ar pour les expressions arithmétiques contenant deux méthodes abstraites : eval de typefloat et print de type unit qui évalue et affiche une expression arithmétique.
    (* 1 *)
    
    class virtual expr_ar () = 
    object
      method virtual eval : unit -> float
      method virtual print : unit -> unit
    end;;
    


  2. Définir une classe concrète constante sous-classe de expr_ar.
    class constante x = 
    object
      inherit expr_ar () 
      val c = x
      method eval () = c
      method print () = print_float c
    end
    
    (* autre solution : *)
    
    class const x = 
    object
      inherit expr_ar () 
      method eval () = x
      method print () = print_float x
    end
    
  3. Définir une sous-classe abstraite bin_op de expr_ar implantant les méthodes eval et print en utilisant deux nouvelles méthodes abstraites oper, de type (float * float) -> float, utilisées par eval, et symbole, de type string, utilisée par print.
    
    class virtual bin_op g d  =
      object(this)
      inherit expr_ar ()
      val fg = g
      val fd = d
      method virtual symbole : string
      method virtual oper : float * float -> float
      method eval () =
        let x = fg#eval()
        and y = fd#eval() in
          this#oper(x,y)
      method print () =
        fg#print();
        print_string (this#symbole);
        fd#print()
    end
    
  4. Définir les classes concrètes add et mul sous-classes de bin_op implantant chacune les méthodes oper et symbole.
    class add x y =
      object
      inherit bin_op x y
      method symbole = "+"
      method oper(x,y) = x +. y
    end;;
    
    class mul  x y =
      object
      inherit bin_op x y
      method symbole = "*"
      method oper(x,y) = x *. y
    end;;
    
  5. Dessiner l'arbre d'héritage.
  6. Écrire une fonction qui prenant une suite de Genlex.token construit un objet de type expr_ar.
    (* 6 *)
    open Genlex;;
    exception Found of expr_ar;;
    
    let rec create accu l =
       let r  =
       ( ( match Stream.next l with
          Float f -> new constante f
        | Int i -> ( new constante (float i) :> expr_ar)
        | Kwd k  -> let v1 = accu#sommet() in
                        accu#depile();
                        let v2 = accu#sommet() in
                          accu#depile();
                          ( match k with
                             "+" -> ( new add v2 v1 :> expr_ar)
                           | "*" -> (  new mul v2 v1 :> expr_ar)
                           | ";" -> raise (Found (accu#sommet()))
                           | _ -> failwith "aux : bas keyword"
                          )
        |  _ -> failwith "aux : bad case"
       ) :> expr_ar) in
         create (accu#empile(r); accu) l
    ;;
    
    let gl = Genlex.make_lexer ["+";"*";";"];;
    
    let run () =
      let s = Stream.of_channel stdin in
      create (new ppile ()) (gl s)
    ;;
    
  7. Tester ce programme en lisant l'entrée standard, en utilisant l'analyseur lexical générique Genlex. On pourra entrer les expressions à évaluer sous forme post-fixée.

Jeu de la vie en objet

On définit les 2 classes suivantes : Les règles de croissance sont décrites au Slide 19 des exercices.

  1. Écrire la classe cell .
    (* 1 *)
    
    class cell a =
      object
        val mutable v = (a : bool)
        method isAlive = v
      end;;
    
  2. Écrire une classe abstraite absWorld qui implante les méthodes display, getCell et setCell et laisse abstraite la méthode nextGen.
    (* 2 *)
    
    class virtual absWorld n m  =
      object(self)
        val mutable tcell = Array.create_matrix n m (new cell false)
        val maxx = n
        val maxy = m
        val mutable gen = 0
        method private dessine(c) =
         if c#isAlive then print_string "*"
         else print_string "."
        method display() =
          for i = 0 to (maxx-1) do
            for j=0 to (maxy -1) do
              print_string " "; self#dessine(tcell.(i).(j))
            done;
            print_newline()
          done
        method getCell(i,j) = tcell.(i).(j)
        method setCell(i,j,c) = tcell.(i).(j) <- c
        method getCells = tcell
      end;;
    
  3. Écrire une classe world , sous-classe de absWorld, qui implante la méthode nextGen selon les règles de croissance.
    
    (* 3 *)
    
    class world n m =
      object(self)
        inherit absWorld n m
        method neighbors(x,y) =
          let r = ref 0 in
            for i=x-1 to x+1 do
              let k = (i+maxx) mod maxx in
                for j=y-1 to y+1 do
                  let l = (j + maxy) mod maxy in
                    if tcell.(k).(l)#isAlive then incr r
                done
             done;
             if tcell.(x).(y)#isAlive then decr r;
             !r
        method nextGen() =
          let w2 = new world maxx maxy in
            for i=0 to maxx-1 do
              for j=0 to maxy -1 do
                let n = self#neighbors(i,j) in
                  if tcell.(i).(j)#isAlive   then
                    (if (n = 2) || (n = 3) then w2#setCell(i,j,new cell true))
                  else
                    (if n = 3 then w2#setCell(i,j,new cell true))
              done
            done;
            tcell <- w2#getCells;
            gen <- gen + 1
    
      end;;
    
  4. Écrire le programme principal qui crée un monde vide, lui ajoute certaines cellules, puis rentre dans une boucle d'interaction qui affiche le monde, attend une interaction, calcule ensuite la génération suivante et repart dans la boucle.
    
    (* 4 *)
    
    exception Fin;;
    
      let main () =
        let a = 10 and b = 12 in
        let w = new world a b in
        begin
          w#setCell(4,4,new cell true);
          w#setCell(4,5,new cell true);
          w#setCell(4,6,new cell true);
          while true do
            w#display();
            if ((read_line()) = "F") then raise Fin
            else w#nextGen()
          done
        end
    ;;
    

Précédent Index Suivant