Licence STS, semestre 4 2009–2010
Langages et Génie Logiciel (Info 223)TP 6
Modules et foncteurs
L’objectif de ce TP est de vous familiariser avec la manipulation de
modules et foncteurs. Ce TP s’appuie sur les primitives du projet,
mais l’utilisation de foncteurs n’est pas forcément la meilleure
solution dans ce cas particulier, aussi il n’est pas demander
d’intégrer ce code directement dans votre projet (cf
question 6 pour des suggestions en rapport avec le
projet).
On utilisera une bibliothèque de vecteurs, on suppose qu’elle se
trouve dans un fichier vector.ml et qu’elle définit le type
t.
Un exemple de telle bibliothèque: Si vous le souhaitez, vous pouvez utiliser la bibliothèque suivante: Interface
de la bibliothèque de vecteurs Code compilé de la
bibliothèque de vecteurs.
-
Définir une signature OBJECT pour représenter
les objets tels que les vaisseaux adverses ou les missiles.
Cette interface définira un type t pour les objets ainsi
que les fonctions suivantes:
-
create qui prend en argument un vecteur représentant la
position où l’objet est créé et renvoie le nouvel objet ;
- display qui prend en argument un objet et l’affiche ;
- move qui applique une étape de déplacement à un objet ;
- out qui prend en argument un objet et renvoie vrai si cet
objet est en dehors de la zone de l’écran.
On rappelle que la syntaxe pour une interface de module est:
module type OBJECT = sig
type t
val create : ...
...
end
- Implanter un module Adv1 qui implante cette signature dans
lequel l’objet est un ennemi bleu, la vitesse initiale de l’objet
est le vecteur (0,5) et cette vitesse ne change pas lors des
déplacements. On rappelle que la syntaxe pour une interface de
module est:
module Adv1 = struct
type t = ...
let create = ...
...
end
- Définir ensuite un foncteur ObjectSet qui prend en
argument un module M de signature OBJECT et qui implante un
module qui définit un type t pour représenter des ensembles
d’objets ainsi que les fonctions suivantes:
-
empty qui construit un ensemble vide d’objets ;
- create qui prend en argument un vecteur x
représentant la position initiale, un vecteur d représentant un
décalage et un entier n et renvoie un ensemble de n objets aux
positions x, x+d, ⋯, x+(n−1)× d. Seuls les objets dans la
zone de l’écran sont conservés ;
- union qui prend en argument deux ensembles d’objets et fait
leur union ;
- display qui prend en argument un ensemble d’objets et l’affiche ;
- move qui prend en argument un ensemble d’objets et leur
applique une étape de déplacement en ne gardant que les objets dans
la zone de l’écran.
Pour implanter ce foncteur on utilisera le foncteur Set.Make pour
représenter des ensembles dont on rappelle les principales
composantes:
module Set :
sig
module type OrderedType = sig type t val compare : t -> t -> int end
module Make :
functor (Ord : OrderedType) ->
sig
type elt = Ord.t
type t
val empty : t
val is_empty : t -> bool
val mem : elt -> t -> bool
val singleton : elt -> t
val add : elt -> t -> t
val union : t -> t -> t
val compare : t -> t -> int
val equal : t -> t -> bool
val iter : (elt -> unit) -> t -> unit
val fold : (elt -> 'a -> 'a) -> t -> 'a -> 'a
val filter : (elt -> bool) -> t -> t
val cardinal : t -> int
val elements : t -> elt list
...
end
end
La syntaxe pour définir un foncteur est:
module ObjectSet (M:OBJECT) = struct
module Ord = ...
module ObjSet = ...
type t = ...
let create = ...
...
end
- Instancier le foncteur ObjectSet sur le module Adv1,
écrire une fonction new_adv qui étant donné un entier n crée
une vague de n vaisseaux répartis sur le haut de l’écran. Tester
les fonctions.
- Optionel On suppose donnée une référence globale
ship correspondant au joueur principal et deux fonctions
ship_pos et ship_speed de type
unit →Vector.t donnant sa position et sa vitesse.
-
Construire une deuxième implémentation Missile de la
signature OBJECT dans laquelle l’objet est un missile, la
vitesse initiale de l’objet est celle du joueur et cette vitesse
diminue de 5% à chaque pas de temps.
- Instancier le module ObjectSet sur le module Missile, écrire une fonction shoot qui crée 3 missiles à partir de la position du joueur (décalés de 3 dans le sens de la vitesse du joueur).
- Tester vos fonctions.
- On pourrait penser introduire un nouveau
module pour représenter un autre type d’adversaire (les enemmis
rouges). Cependant cette approche n’est pas recommandée car l’état
du système devrait alors contenir un ensemble de vaisseaux bleus, un
ensemble de vaisseaux rouges… ce qui empèche de factoriser le
code. Pour représenter différentes catégories d’adversaires, il est
préférable d’utiliser un unique type de données avec un champs supplémentaire
permettant de distinguer les comportements.
Pour cela on peut ajouter à la signature OBJECT un type
abstrait info qui permet de caractériser l’adversaire (couleur,
vitesse initiale, frottement …) et la fonction create
prendra alors en argument une position et un objet de type
info.
-
Reprendre votre développement pour utiliser cette représentation et
définir un module Adv qui permet de représenter à la fois des
adversaires bleus et des adversaires rouges.
- On remarque que le type info peut également être utilisé
pour distinguer les adversaires des missiles et donc qu’il n’est pas
nécessaire dans ce cas d’avoir deux modules différents. Si le
foncteur ObjectSet est instancié sur un unique module, alors
la construction de foncteur est inutile. Il suffit d’avoir un
fichier object.ml pour la représentation des objets et un
fichier objectSet.ml pour les ensembles d’objets. On pourra
ensuite spécialiser les fonctions de création pour les différentes
catégories d’objets.
Ce document a été traduit de LATEX par HEVEA