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 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;;
- 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;;
- 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;;
- 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;;
- 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 message.
- 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.
-
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;;
- 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
- 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
- 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;;
- Dessiner l'arbre d'héritage.
- É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)
;;
- 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.
-
Écrire la classe cell .
(* 1 *)
class cell a =
object
val mutable v = (a : bool)
method isAlive = v
end;;
- É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;;
- É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;;
- É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
;;