Exercices
Piles en objet
On reprend l'exemple des piles vu précédemment à la sauce objet.
-
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
- 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 = ()
- 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
- 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
- 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 :
-
Dessiner les relations entre classes
- Tracer les différents envois de messages.
- 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.
-
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
-
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
-
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
-
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
- Dessiner l'arbre d'héritage.
- É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>
- 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 :
-
É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
- É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
- É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
- É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>