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
=
1
0
and
b
=
1
2
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>