Maitrise d'Informatique
Module POD
Programmation Objet et Distribution
Université Pierre et Marie Curie
Année 2000--2001
Partiel du 4 décembre 2000
Notes manuscrites et polycopiés autorisés - Durée 2 heures
1 Exercice 1 : conjecture de Syracuse
Soit la suite suivante :
" x Î N*
|
u0 = |
x |
un = |
ì í î |
1 |
si un-1 vaut 1 |
un-1/2 |
si un-1 est pair |
3*un-1+1 |
si un-1 est impair |
|
|
|
|
la conjecture de Syracuse indique que cette suite converge toujours vers 1.
On appelle altitude maximale la plus grande valeur trouvée pour un un
et vol en altitude le nombre de un de valeur plus grande que le u0 initial.
On définit la classe syrac
suivante qui calcule les différents ui jusqu'à la convergence vers 1.
source |
inférence de types |
class syrac x =
object(self)
val mutable u_0 = x
val mutable u_n = x
val mutable duree = 0
method get_u_0 = u_0
method set_u_0 u = u_0 <- u
method get_duree = duree
method set_duree d = duree <- d
method private une_etape () =
if u_n mod 2 = 0
then u_n <- u_n / 2
else u_n <- 3 * u_n + 1
method calcule () =
if u_n = 1 then ()
else
begin
self#une_etape();
duree <- duree + 1;
self#calcule()
end
end;;
|
class syrac :
int ->
object
val mutable duree : int
val mutable u_0 : int
val mutable u_n : int
method calcule : unit -> unit
method get_duree : int
method get_u_0 : int
method set_duree : int -> unit
method set_u_0 : int -> unit
method private une_etape : unit -> unit
end
|
-
Indiquer les différentes valeurs prises par la variable d'instance
u_n
de l'objet x dans le programme suivant :
# let x = new syrac 7;;
val x : syrac = <obj>
# x#calcule();;
- : unit = ()
# x#get_duree;;
- : int = 16
Solution 1.1
7
22
11
34
17
52
26
13
40
20
10
5
16
8
4
2
1
- On cherche à conserver la plus haute valeur atteinte (altitude maximale) ainsi que le nombre de valeurs au dessus du nombre initial (vol en altitude). Ecrire une sous-classe
syrac_plus
de syrac qui permet de conserver ces valeurs et d'y avoir accès à la fin d'un calcul par les méthodes get_alt
et get_vol
.
Solution 1.2
fichier : q12.ml |
class syrac_plus x =
object(self)
inherit syrac x as super
val mutable vol = 0
val mutable alt = 0
method get_vol = vol
method set_vol v = vol <- v
method get_alt = alt
method set_alt a = alt <- a
method calcule() =
if u_n = 1 then ()
else
begin
self#une_etape();
duree <- duree + 1;
if alt < u_n then alt <- u_n;
if u_n > u_0 then vol <- vol + 1;
self#calcule()
end
end;;
|
- Que retournent alors les appels suivants :
let x = syrac_plus 7;;
x#get_duree;;
x#get_alt;;
x#get_vol;;
Solution 1.3
(* Attention erreur d'énoncé signalée pendant le partiel :
il faut remplacer la 1ere ligne par les 2 suivantes :
let x = new syrac_plus 7;;
x#calcule();;
*)
# let x = new syrac_plus 7;;
val x : syrac_plus = <obj>
# x#calcule();;
- : unit = ()
# x#get_duree;;
- : int = 16
# x#get_alt;;
- : int = 52
# x#get_vol;;
- : int = 12
Exercice 2 : Observateurs
Le but de cet exercice est d'implanter le motif de conception (design pattern) ``observateur''.
Soit le programme suivant :
source |
inférence de types |
class virtual obs_abs () =
object
method virtual miseajour : unit -> unit
end;;
class sujet_gen () =
object
val mutable lobs = ([] : obs_abs list)
method attache o = lobs <- o :: lobs
method detache o =
lobs <- List.filter (fun x -> x <> o) lobs
method private notify () =
List.iter (fun x -> x#miseajour()) lobs
end;;
class ['a] sujet (x:'a) =
object
inherit sujet_gen ()
val mutable etat = x
method acqEtat = etat
end;;
class ['a] obs (x : 'a sujet) =
object(self)
inherit obs_abs ()
val s = x
val mutable etat = x#acqEtat
initializer s#attache(self :> obs_abs)
method miseajour () = etat <- s#acqEtat
end;;
let s = new sujet (0.0, 0.0);;
let o1 = new obs s;;
let o2 = new obs s;;
s#attache(o1);;
s#attache(o2);;
|
class virtual obs_abs :
unit ->
object
method virtual miseajour : unit -> unit
end
class sujet_gen :
unit ->
object
val mutable lobs : obs_abs list
method attache : obs_abs -> unit
method detache : obs_abs -> unit
method private notify : unit -> unit
end
class ['a] sujet :
'a ->
object
val mutable etat : 'a
val mutable lobs : obs_abs list
method acqEtat : 'a
method attache : obs_abs -> unit
method detache : obs_abs -> unit
method private notify : unit -> unit
end
class ['a] obs :
'a sujet ->
object
val mutable etat : 'a
val s : 'a sujet
method miseajour : unit -> unit
end
val s : (float * float) sujet
val o1 : (float * float) obs
val o2 : (float * float) obs
|
-
Ecrire le schéma des relations (agrégation et héritage) entre ces quatre classes.
- Si la méthode notify n'était pas private, quelles seraient les actions déclenchées par l'appel suivant :
s#notify()
?
- Soient les classes concrètes sujet_syrac et obs_syrac :
source |
inférence de types |
class sujet_syrac a b =
object(self)
inherit
[syrac_plus] sujet (new syrac_plus a)
as super
val mutable debut = a
val fin = b
method run () =
while debut <= fin do
print_int debut; print_newline();
etat <- new syrac_plus debut;
etat#calcule();
self#notify();
debut <- debut + 1
done
end;;
class virtual obs_syrac s =
object(self)
inherit [syrac_plus] obs s as super
method miseajour () =
if self#check() then
begin
super#miseajour();
self#display()
end
method virtual check : unit -> bool
method virtual display : unit -> unit
end;;
|
class sujet_syrac :
int ->
int ->
object
val mutable debut : int
val mutable etat : syrac_plus
val fin : int
val mutable lobs : obs_abs list
method acqEtat : syrac_plus
method attache : obs_abs -> unit
method detache : obs_abs -> unit
method private notify : unit -> unit
method run : unit -> unit
end
class virtual obs_syrac :
syrac_plus sujet ->
object
val mutable etat : syrac_plus
val s : syrac_plus sujet
method virtual check : unit -> bool
method virtual display : unit -> unit
method miseajour : unit -> unit
end
|
attacher ces classes au schéma de relations de la question 1.
-
Ecrire trois classes concrètes obs_duree, obs_vol et
obs_alt héritant de la classe
obs_syrac
qui
mettent à jour leur état interne si l'état passé par le sujet est
plus grand respectivement pour
la durée, le vol et l'altitude. En cas de changement d'état interne
d'une instance de ces classes, il y a un affichage du nombre intitial et de la valeur qui a provoqué ce changement.
Solution 2.4
class obs_duree s =
object
inherit obs_syrac s as super
method check () =
etat#get_duree < s#acqEtat#get_duree
method display () =
print_string "durée de ";
print_int etat#get_u_0; print_string " = ";
print_int etat#get_duree;
print_newline()
end
class obs_vol s =
object
inherit obs_syrac s as super
method check () =
etat#get_vol < s#acqEtat#get_vol
method display () =
print_string "vol de ";
print_int etat#get_u_0; print_string " = ";
print_int etat#get_vol;
print_newline()
end
class obs_alt s =
object
inherit obs_syrac s as super
method check () =
etat#get_alt < s#acqEtat#get_alt
method display () =
print_string "altitude de ";
print_int etat#get_u_0; print_string " = ";
print_int etat#get_alt;
print_newline()
end
- Qu'affiche l'exécution du programme suivant :
let sx = new sujet_syrac 1 8;;
let od = new obs_duree (sx :> syrac_plus sujet);;
let ov = new obs_vol (sx :> syrac_plus sujet);;
let oa = new obs_alt (sx :> syrac_plus sujet);;
sx#run();;
Solution 2.5
# let sx = new sujet_syrac 1 8;;
val sx : sujet_syrac = <obj>
# let od = new obs_duree (sx :> syrac_plus sujet);;
val od : obs_duree = <obj>
# let ov = new obs_vol (sx :> syrac_plus sujet);;
val ov : obs_vol = <obj>
# let oa = new obs_alt (sx :> syrac_plus sujet);;
val oa : obs_alt = <obj>
# sx#run();;
1
2
altitude de 2 = 1
durée de 2 = 1
3
altitude de 3 = 16
vol de 3 = 5
durée de 3 = 7
4
5
6
durée de 6 = 8
7
altitude de 7 = 52
vol de 7 = 12
durée de 7 = 16
8
- : unit = ()
- Dans la classe
obs
expliquez la contrainte de type
dans l'initialiseur.
Exercice 3 : Observateurs distants
Dans ce dernier exercice, on cherche à implanter des observateurs répartis. Ceux-ci communiquent avec le sujet via des sockets TCP/IP selon un schéma de clients (observateurs) et serveur(sujet).
Le protocole utilisé est simple. La connexion d'un client au serveur est considérée comme un attachement et la fermeture de la socket par le client comme un détachement de la liste des observateurs. La communication sur la socket correspond à une mise à jour et la valeur de l'état transite alors sur la socket dans le sens serveur vers client.
Le serveur doit effectuer 2 tâches : être à l'écoute de connexions
de possibles clients
et effectuer un certain travail.
En parallèle les clients effectuent aussi 2 tâches : être à l'écoute de
l'envoi d'un changement d'état de la valeur observée sur le serveur
et effectuer un certain travail.
On se donne les deux classes abstraites suivantes : serv_gen
et client_gen
:
classe serv_gen |
classe client_gen |
let get_my_addr () =
(Unix.gethostbyname(Unix.gethostname())).Unix.h_addr_list.(0) ;;
class virtual serv_gen p =
object (self)
val port = p
val mutable sock = ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0
val mutable id_ecoute = Thread.self ()
val mutable id_travaille = Thread.self ()
initializer
let mon_adresse = get_my_addr ()
in Unix.bind sock (Unix.ADDR_INET(mon_adresse,port)) ;
Unix.listen sock 3
method private client_addr = function
Unix.ADDR_INET(host,_) -> Unix.string_of_inet_addr host
| _ -> "Unexpected client"
method virtual ecoute : unit -> unit
method virtual travaille : unit -> unit
method start () =
id_ecoute <- Thread.create ( fun () -> self#ecoute()) ();
print_string "ATTENTE "; print_newline();
Thread.delay(30.0);
self#travaille()
method stop() =
Thread.kill id_ecoute;
exit 0; ()
end ;;
|
class virtual client_gen m p =
object(self)
val port = p
val serveur = m
val mutable adr_serv =
(Unix.gethostbyname m).Unix.h_addr_list.(0)
val mutable sock =
ThreadUnix.socket Unix.PF_INET Unix.SOCK_STREAM 0
val mutable id_ecoute = Thread.self ()
val mutable id_travaille = Thread.self ()
initializer
ThreadUnix.connect sock (Unix.ADDR_INET(adr_serv, port))
method virtual ecoute : unit -> unit
method virtual travaille : unit -> unit
method start () =
id_ecoute <- Thread.create ( fun () -> self#ecoute()) ();
self#travaille()
method stop() =
Thread.kill id_ecoute;
exit 0; ()
end ;;
|
ainsi que la classe abstraite com_gen
pour la communication entre serveur et client et enfin les classes sujet_serveur
et syrac_comc
:
classe com_gen |
classes sujet_serveur et com_syrac |
class virtual com_gen (sd : Unix.file_descr) =
object(self:'a)
method virtual to_string : unit -> string
method virtual of_string : string -> unit
method miseajour () = self#send()
method send () =
let message = self#to_string() in
let lm = String.length message in
let lms = (string_of_int lm)^"\n" in
ThreadUnix.write sd lms 0 (String.length lms);
ThreadUnix.write sd message 0 lm;
()
method receive () =
let buffer = String.create 1 in
let r = ref "" in
while (buffer <> "\n") do
ThreadUnix.read sd buffer 0 1;
r := !r ^ buffer
done;
let l = int_of_string !r in
let str = String.create l in
ThreadUnix.read sd str 0 l;
self#of_string str
end;;
|
class virtual ['a] sujet_serveur_abs (x :'a) p =
object
inherit serv_gen p as serv_super
val mutable etat = x
val mutable lobs = ([] : com_gen list)
method getEtat = etat
method attache o = lobs <- o :: lobs
method detache o =
lobs <- List.filter (fun x -> x <> o) lobs
method private notify () =
List.iter (fun x -> x#send()) lobs
end;;
class obs_com_syrac sd (x:syrac_plus sujet_serveur_abs) =
object(self)
inherit com_gen sd as super
val s = x
val e = x#getEtat
initializer s#attache(self :> com_gen)
method to_string () =
Marshal.to_string (e#get_u_0,e#get_duree,
e#get_alt,e#get_vol) []
method of_string ms =
let n,d,a,v = Marshal.from_string ms 0 in
e#set_u_0(n);
e#set_duree(d);
e#set_alt(a);
e#set_vol(v)
end;;
|
-
Indiquer les relations entre les classes définies précédemment.
- Ecrire la classe concrète
sujet_serveur_syrac
qui lance le
calcul de la suite sur un intervalle d'entiers et envoie à chaque étape
les valeurs des variables d'instance aux clients enregistrés.
Solution 3.2
class sujet_serveur_syrac a b p =
object(self:'a)
inherit [syrac_plus] sujet_serveur_abs (new syrac_plus a) p as super
val mutable debut = a
val fin = b
method ecoute () =
while(true) do
let (sd,sa) = ThreadUnix.accept sock in
new obs_com_syrac sd (self :> syrac_plus sujet_serveur_abs)
done
method travaille () =
while debut <= fin do
print_int debut; print_newline();
etat <- new syrac_plus debut;
etat#calcule();
self#notify();
debut <- debut + 1
done
end;;
- Ecrire le lancement du serveur sur le port 3000.
Solution 3.3
let go () =
let sss = new sujet_syrac_serv 1 7 3000 in
sss#start();;
go();;
- Ecrire la ou les classes nécessaires pour un client à l'écoute d'un serveur
de la classe
sujet_serveur_syrac
.
- Indiquer ce qu'il faut ajouter à la classe
sujet_serveur
pour tenir compte
de la déconnexion d'un client.
- Que se passe-t-il quand le serveur a fini son calcul?
- Modifier la classe client pour que la méthode
travaille
soit
en attente de modification de changement de l'état.
- Indiquer comment modifier le serveur pour que la méthode
calcule
ne fasse son travail qu'une fois qu'il y a au moins trois clients connectés.
Ce document a été traduit de LATEX par
HEVEA.