Skip to content
Snippets Groups Projects
tp7.md 6.94 KiB
Newer Older
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
# TP7 - Application et configuration par fonctions
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
On va implémenter une petite application simple en OCaml.
Même si l'application tiendra en un seul fichier, elle comprendra plusieurs parties regroupées en _modules_.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
## 1. Application et modules
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
On souhaite implémenter une application de gestion de la fabrication des jouets de Noël par les lutins du Père-Noël.
Bien que l'application soit modeste on souhaite pouvoir la faire grossir selon les besoins.
On va donc dès le départ la diviser en différents modules, le code de l'application contenant simplement des appels aux bonnes fonctions des autres modules.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Les différentes parties de l'application intiale sont les suivantes:
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
- Un module `Association` de gestion des associations clé-valeur basé d'abord sur des listes, puis sur des arbres binaires de recherche. On veut pouvoir, à terme (mais pas dans ce TP), remplacer les usages de ce module par un module de la bibliothèque standard OCaml.
- Un module `Usine` contenant les types liés au métier de l'application: lutins, jouets, etc. Ce module contiendra également les différentes fonctions utilisées pour gérer l'usine.
- Un module `LutinsApp` qui va contenir le code de gestion des arguments en ligne de commande
- Enfin le code hors de ces modules lancera simplement l'application.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Tous ces modules seront placés dans le fichier `usinelutins.ml`.
On peut résumer les dépendances simples des modules de cette application via le diagramme suivant (la flèche signifie "est utilisé par"):
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
```mermaid
graph TD
BRANDEL SYLVAIN's avatar
BRANDEL SYLVAIN committed
  A[Association] --> B[Usine]
  B --> C[LutinsApp]
  C --> D[code directement dans usinelutins.ml]
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
```
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
### 2. Premiers modules
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Créer un fichier `usinelutins.ml`. À titre d'exemple de module on donne le code suivant, à placer au début du fichier `usinelutins.ml`:
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
```ocaml
module Association = struct
  (* Type pour représenter des dictionnaires (ou map ou association) dont la clé est une string *)
  type 'a t = (string * 'a) list

  (* Fonction pour chercher dans un dictionnaire. get d k renvoie None si k n'est pas une clé de d,
      Some x si x est la valeur associée à k dans d. *)

  let get : 'a t -> string -> 'a option = fun d k -> List.assoc_opt k d

  (* Ajoute un couple clé/valeur dans un dictionnaire, si la clé est déjà dans le dictionnaire,
      change en v la valeur qui sera renvoyée par get *)
  let put : 'a t -> string -> 'a -> 'a t = fun d k v -> (k, v) :: d

  (* Le dictionnaire vide *)
  let empty : 'a t = []

  (* Supprime les valeurs associées à la clé k dans le dictionnaire. *)
  let rec delete : 'a t -> string -> 'a t =
   fun d k ->
    match d with
    | [] -> []
    | (k', v') :: rest ->
        if k = k' then delete rest k else (k', v') :: delete rest k
end
```
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Créer un module `Usine` à la suite du module `Association`. Dans ce module, créer:
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
- un type `jour` pour représenter les jours de la semaine (`Lundi`, `Mardi`, etc)
- une fonction `string_of_jour: jour -> string`
- une fonction `jour_opt_of_string: string -> jour option`
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
> Ajouter des `assert` pour tester `string_of_jour` et `jour_opt_of_string`.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
### 3. Utilisation du module `Usine`
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
On souhaite maintenant utiliser ce module. Pour cela il faut:
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
1. Écrire le code qui l'utilise après la définition du module
2. Faire précéder le nom d'une fonction, d'un type ou d'un constructeur par le nom du module, par exemple on écrira `Usine.Lundi`.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Ajouter le code suivant à la fin de `usinelutins.ml`:
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
```ocaml
let usage () = print_endline "\nUsage: ocaml usinelutins.ml jour"
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
let run args =
  match args with
  | _pgm :: jour_s :: _ -> print_endline "Bonjour, nous sommes un certain jour"
  | _ -> usage ()
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
let _ = run (Array.to_list Sys.argv)
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
> Modifier ce code afin afficher en plus `Nous sommes ` suivi d'un jour de la semaine traduit en string via un appel à `string_of_jour`.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
> Vérifier le bon fonctionnement du programme en lançant `ocaml usinelutins.ml`. Modifier le code de façon à ce que le jour choisi soit obtenu à partir de `jour_s`. Gérer par un affichage approprié le cas ou `jour_s` n'est pas un jour.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
### 4. Enrichir le module `Usine`
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
On veut maintenant définir au sein de l'usine de fabrication de jouets une notion de _configuration_ de la journée. La configuration d'une journée permet de connaître deux choses: pour chaque lutin, le jouet qu'il va fabriquer et pour chaque lutin et chaque jouet la quantité qu'il peut fabriquer. Les jouets et les lutins étant représentés par des chaînes de caractères, il se peut qu'un lutin ou un jouet passé en argument n'existe pas. On utilise donc une `option` pour gérer cette situation.

On va maintenant créer dans le module `Usine` un type `configuration` qui sera simplement une paire de fonctions. La première aura le type `string -> string option`, la seconde aura le type `string -> string -> int option`.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Définir ce type le module `Usine`:
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed

```ocaml
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
type configuration = (string -> string option) * (string -> string -> int option);;
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Ajouter les fonctions suivantes dans le module `Usine`:
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
- `mk_configuration: (string -> string option) -> (string -> string -> int option) -> configuration` cette fonction va fabriquer une paire avec ses deux arguments.
- `get_jouet: configuration -> (string -> string option)` cette fonction va extraire le premier élément de la paire
- `get_nb_jouets: configuration -> (string -> string -> int option)` cette fonction va extraire le deuxième élément de la paire
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Tester ces fonctions avec des `assert` en utilisant une fonction qui renvoie toujours "toupie" pour le choix du jouet et toujours `42` pour le nombre de jouets.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
### 5. Codage de l'application de gestion des jouets - début
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
On dispose à présent des éléments de langage pour pouvoir coder le début de l'application de gestion de l'usine de jouets.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Dans le module `Usine`, créer une configuration basée sur des `assoc` pour créer une variable globale pour y stocker, pour chaque jour, une configuration. Essayer de faire varier les fonctions de la configuration selon le jour.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Créer également une variable globale contenant la liste des noms des lutins.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Créer une fonction `calcule_jouets_config: configuration -> (string,int) list` qui indique pour chaque jouet combien d'exemplaires ont été fabriqué, en excluant les jouets fabriqués zéro fois.
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Enfin créer un dernier module `LutinsApp` qui contiendra:
- une fonction `affiche_jouets: (string * int) list -> string` qui calculera une chaîne d'affichage de la sortie de la fonction `calcule_jouets_config`;
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Modifier la fonction `run: string list -> unit` pour affichera (via `print_endline`) les jouets produits durant le jour passé en argument.
Avec l'implémentation actuelle, il est possible que certains jouets s'affichent plusieurs fois, cela sera corrigé avec la section suivante.

COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
### 6. Optimisation du module Association
COQUERY EMMANUEL's avatar
COQUERY EMMANUEL committed
Changer le type et le code du module `Association` de façon à utiliser des arbres binaires de recherche à la place de listes de paires. Le code du reste de l'application ne devrait pas changer.