Objectif: écriture et utilisation d’une classe d’objets, utilisation de tableaux (de type primitifs).

Avant tout chose organisez votre espace disque afin de pouvoir par la suite retrouver facilement le travail effectué lors de cette séance de TP. Pour cela, dans le répertoire PLAI/Java créé lors du TP1, ajoutez un sous répertoire TP05 dans lequel vous rangerez le travail effectué au cours ce TP.

Contexte

On désire introduire la notion d'ensemble à des enfants et les initier aux notions d'appartenance, d'intersection, d'union et d'inclusion. Dans ce cadre, on souhaite écrire une application éducative (à l'aide du langage Java) qui permettra aux enfants de manipuler des ensembles de lettres (lettres de \('a'\) à \('z')\) sans répétition (une lettre ne peut être présente qu'une seule fois).

Pour avoir une définition formelle (mathématique) des opérations sur les ensembles que vous devrez réaliser, vous pouvez vous reporter à l'annexe 'Rappels sur les opérateurs ensemblistes' située en fin de ce sujet de TP.

Partie 1 : Spécification de l'interface publique de la classe EnsembleDeLettres

Pour représenter les ensembles de lettres on définit une classe EnsembleDeLettres, offrant les services (constructeurs et méthodes) suivants:

Opérations de création:

  • Créer un nouvel ensemble, initialement vide,

  • Créer un nouvel ensemble de lettres dont les éléments sont tirés au hasard,

  • Créer un nouvel ensemble à partir d’une chaîne de caractères. L’ensemble contient toutes les lettres présentes dans la chaîne.

Opérations sur un ensemble:

  • Afficher sur la console l’ensemble. L'affichage de l'ensemble se fait sous la forme suivante :

    • les lettres présentes dans l'ensemble sont affichées séparées par des espaces. Une accolade ouvrante ('{') précédant la première lettre, et une accolade fermante ('}') suivant la dernière lettre. Si l'ensemble est vide seules les accolades sont affichées.

    • Exemples:

      • {} affichage produit pour un ensemble vide.

      • {'a', 'm', 't', 'z'} affichage produit pour un ensemble contenant les lettres 'a', 'm', 't' et 'z'.

  • Tester si l’ensemble est vide.

  • Obtenir le cardinal de l’ensemble (nombre d’éléments de l’ensemble).

  • Tester l’inclusion de l’ensemble dans un autre.

  • Tester si une lettre est présente ou non dans l’ensemble.

  • Effectuer les opérations ensemblistes suivantes (voir annexe pour une définition des opérations )

    • Créer un nouvel ensemble dont les éléments sont les éléments présents dans l’union de l’ensemble avec un autre ensemble donné.

    • Créer un nouvel ensemble correspondant à la différence de l’ensemble avec un autre ensemble donné.

    • Créer un nouvel ensemble correspondant à l’union disjointe de l’ensemble avec un autre ensemble donné.

    • Créer un nouvel ensemble dont les éléments sont les éléments présents dans l’intersection de l’ensemble avec un autre ensemble donné.

Exercice 1: Sans vous soucier de la manière dont l'ensemble sera réprésenté en interne dans la classe EnsembleDeLettres, donnez l'interface publique de cette classe (signature des constructeurs et méthodes publiques, commentaires javadoc associés).

Pour effectuer cette spécification, procédez comme suit

  1. Créez une nouvelle classe EnsembleDeLettres (reportez vous au TP 03 pour voir comment créer un projet Java et une classe avec l'IDE VSCode).

  2. Dans cette classe écrivez les signatures des constructeurs et des méthodes et les commentaires documentant associés.

  3. Générez la javadoc de cette classe. Pour pouvoir effectuer cette génération de la documentation il faut pouvoir compiler votre classe EnsembleDeLettres sans erreur. Aussi, afin que le code de votre classe ne présente pas d'erreurs de syntaxe empêchant sa compilation, procédez comme suit :

    • Pour les constructeurs et les méthodes dont le type de retour est void (actions) vous laisserez leur corps vide.

    • Pour les autres méthodes (fonctions) vous vous contenterez d'écrire une simple instruction return false, return 0 ou return null selon que type de retour est un entier, un booléen ou un type objet.

Une fois votre spécification terminée, faites la valider par votre enseignant avant de passer à l'exercice suivant.

Partie 2 : Implémentation de la classe EnsembleDeLettres

Pour l'implémentation de la classe EnsembleDeLettres il faut définir une structure de données repésentant un ensemble de lettres. Pour cela il est décidé d'utiliser un tableau present de 26 booléens, present[i] ayant la valeur true si la ième lettre de l’alphabet appartient à l'ensemble et false sinon.

Par exemple l'ensemble {‘a’, ‘c’, ‘f’, ‘g’, ‘j’, ‘m’, ‘n’, ‘s’, ‘u’, ‘w’, ‘z’} sera représenté par le tableau ci-dessous

representation d'un ensemble
Figure 1: representation d'un ensemble de lettres à l'aide d'un tableau de booléens

Exercice 2: Comment effectuer la correspondance entre l'index dans le tableau et la lettre correspondante (et inversement) ? Etudiez et proposez une ou plusieurs solutions que vous pourrez ensuite comparer avec la solution ci-dessous.

Le problème est de trouver une correspondance qui permet de passer d'une lettre (minuscule) à sa position dans le tableau et inversement d'une position dans le tableau à la lettre associée.

Pour effectuer cette correspondance on peut se servir du codage des caractères. En effet, Java supporte les caractères unicodes et utilise en interne un codage UTF-16, où chaque caractère est codé sur une suite de un ou deux mots de 16 bits.

Le standard Unicode est constitué d'un répertoire de 128 172 caractères, couvrant une centaine d’écritures, et attribue à chaque caractère un identifiant nommé code point. Unicode utilise la notation hexadécimale prefixée par «U+» pour représenter un code point. Les 127 premiers caractères d'Unicode correspondent exactement à l'ensemble des caractères Ascii :

  • le caractère 'a' possède le code point U+0061 ce qui correspond au nombre 97,
  • le caractère 'b' possède le code point U+0062 ce qui correspond au nombre 98,
  • etc...

On peut donc passer directement d'un caractère à un entier en effectuant un simple transtypage.

char c = 'a'; 
int code = (int) c;
System.out.print(code);   ---> 97

inversement

int code = 97;
char c = (char) code;
Systeme.out.println(c); ---> a

De même lorsque un caractère est utilisé dans un expression arithmétique avec l'opérateur + ou -, il est subsitué par son code. Ainsi l'expression 'b' - 'a' vaut 1 et inversement l'expression 'a' + 1 donne 98 (le code de 'b').

Partant de ces constatations, il est donc aisé d'écrire les fonctions qui permettent de passer d'un caractère ('a' à 'z') à son rang dans le tableau et inversement de passer du rang dans le tableau (0 à 25) à la lettre minuscule correspondante.

/**
* pour un caractère donné renvoie sa position dans le tableau 
*(la position  0 correspondant à la lettre 'a').
*
* @param c le caractère dont on veut la position
* @return la position de ce caractère dans le tableau
*/
private int toCode(char c) {
    return c - 'a';
}

/**
* pour une position donnée dans le tableau renvoie le caractère correspondant dans l'alphabet
* (le caractère 'a' correspond à la position 0)
*
* @param code la position dans le tableau dont on veut connaître le caractère correspondant
* @return le caractère correspondant à cette position
*/
private char toChar(int code) {
    return (char) ('a' + code);
}

Exercice 3: En utilisant la réprésentation proposée ci-dessus, complétez le code de la classe EnsembleDeLettres que vous avez créée lors de l'exercice 1 en implémentant les différents constructeurs et les différentes méthodes que vous avez proposés (et en respectant les spécifications). Parallèment ecrivez un programme (méthode main) dans une classe TestEnsembleDeLettres vous permettant de tester le bon fonctionnement votre classe.

Conseil : Pour réaliser et tester la classe EnsembleDeLettres vous procéderez de manière incrémentale. N’écrivez pas toute la classe EnsembleDeLettres puis ensuite le code permettant de la tester. Travaillez de manière progressive : ajoutez une fonctionnalité (constructeur ou méthode) à la classe EnsembleDeLettres et écrivez immédiatement dans TestEnsembleDeLettres le code permettant de tester cette dernière. Une fois que vous avez vérifié que l’exécution de votre test est correcte, passez à l'implémentation de la fonctionnalité suivante.

Annexe : Rappels sur les opérateurs ensemblistes

opérateurs ensemblistes