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 d'Objective CAML implantant des listes d'entiers et disposant des méthodes empile, depile, sommet et taille.

    # 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 ;;
    exception PileVide
    class intpile :
    unit ->
    object
    val p : int list ref
    method depile : unit -> unit
    method empile : int -> unit
    method sommet : unit -> int
    method taille : unit -> int
    end


  2. Créer une instance contenant 3 et 4 comme éléments de la pile.

    # let p = new intpile () ;;
    val p : intpile = <obj>
    # p#empile 3 ;;
    - : unit = ()
    # p#empile 4 ;;
    - : unit = ()


  3. Définir une nouvelle classe pile contenant des éléments répondant à la méthode print : unit -> unit.

    # 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 ;;
    class pile :
    unit ->
    object
    val p : < print : unit -> unit > list ref
    method depile : unit -> unit
    method empile : < print : unit -> unit > -> unit
    method sommet : unit -> < print : unit -> unit >
    method taille : unit -> int
    end


  4. Définir une classe paramétrée ['a] pile avec les mêmes méthodes.

    # 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 ;;
    class ['a] ppile :
    unit ->
    object
    val p : 'a list ref
    method depile : unit -> unit
    method empile : 'a -> unit
    method sommet : unit -> 'a
    method taille : unit -> int
    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 messages.

  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ée 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 type float et print de type unit qui respectivement évalue et affiche une expression arithmétique.

    # class virtual expr_ar () =
    object
    method virtual eval : unit -> float
    method virtual print : unit -> unit
    end ;;
    class virtual expr_ar :
    unit ->
    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 ;;
    class constante :
    float ->
    object
    val c : float
    method eval : unit -> float
    method print : unit -> unit
    end

    (* autre solution : *)

    # class const x =
    object
    inherit expr_ar ()
    method eval () = x
    method print () = print_float x
    end ;;
    class const :
    float -> object method eval : unit -> float method print : unit -> unit 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ée 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 ;;
    class virtual bin_op :
    (< eval : unit -> float; print : unit -> 'b; .. > as 'a) ->
    (< eval : unit -> float; print : unit -> unit; .. > as 'c) ->
    object
    val fd : 'c
    val fg : 'a
    method eval : unit -> float
    method virtual oper : float * float -> float
    method print : unit -> unit
    method virtual symbole : string
    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 add :
    (< eval : unit -> float; print : unit -> 'b; .. > as 'a) ->
    (< eval : unit -> float; print : unit -> unit; .. > as 'c) ->
    object
    val fd : 'c
    val fg : 'a
    method eval : unit -> float
    method oper : float * float -> float
    method print : unit -> unit
    method symbole : string
    end

    # class mul x y =
    object
    inherit bin_op x y
    method symbole = "*"
    method oper(x,y) = x *. y
    end ;;
    class mul :
    (< eval : unit -> float; print : unit -> 'b; .. > as 'a) ->
    (< eval : unit -> float; print : unit -> unit; .. > as 'c) ->
    object
    val fd : 'c
    val fg : 'a
    method eval : unit -> float
    method oper : float * float -> float
    method print : unit -> unit
    method symbole : string
    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.

    # open Genlex ;;
    # exception Found of expr_ar ;;
    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"
    in
    create (accu#empile (r :> expr_ar); accu) l ;;
    val create :
    < depile : unit -> 'a; empile : expr_ar -> 'b; sommet : unit -> expr_ar;
    .. > ->
    Genlex.token Stream.t -> 'c = <fun>

    # let gl = Genlex.make_lexer ["+"; "*"; ";"] ;;
    val gl : char Stream.t -> Genlex.token Stream.t = <fun>

    # let run () =
    let s = Stream.of_channel stdin in
    create (new ppile ()) (gl s) ;;
    val run : unit -> 'a = <fun>


  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 :
  1. Écrire la classe cell.

    # class cell a =
    object
    val mutable v = (a : bool)
    method isAlive = v
    end ;;
    class cell : bool -> object val mutable v : bool method isAlive : bool end


  2. Écrire une classe abstraite absWorld qui implante les méthodes display, getCell et setCell et laisse abstraite la méthode nextGen.

    # 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 ;;
    class virtual absWorld :
    int ->
    int ->
    object
    val mutable gen : int
    val maxx : int
    val maxy : int
    val mutable tcell : cell array array
    method private dessine : cell -> unit
    method display : unit -> unit
    method getCell : int * int -> cell
    method getCells : cell array array
    method setCell : int * int * cell -> unit
    end


  3. Écrire une classe world, sous-classe de absWorld, qui implante la méthode nextGen selon les règles de croissance.

    # 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 ;;
    class world :
    int ->
    int ->
    object
    val mutable gen : int
    val maxx : int
    val maxy : int
    val mutable tcell : cell array array
    method private dessine : cell -> unit
    method display : unit -> unit
    method getCell : int * int -> cell
    method getCells : cell array array
    method neighbors : int * int -> int
    method nextGen : unit -> unit
    method setCell : int * int * cell -> unit
    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.

    # exception Fin;;
    exception Fin

    # let main () =
    let a = 10 and b = 12 in
    let w = new world a b in
    w#setCell(4,4,new cell true) ;
    w#setCell(4,5,new cell true) ;
    w#setCell(4,6,new cell true) ;
    try
    while true do
    w#display() ;
    if ((read_line()) = "F") then raise Fin else w#nextGen()
    done
    with Fin -> () ;;
    val main : unit -> unit = <fun>

Précédent Index Suivant