Previous Up Next

Chapter 3  Les états mémoires

3.1  Aspects théoriques des états mémoires

Les paragraphes suivants ont pour but d'aborder la partie réalisation et implantation des états mémoires d'un point de vue théorique. Cependant, afin d'illustrer les propos, des allusions à la réalisation technique ont été faites.

3.1.1  But de la structure de données

Les états mémoires ont pour but de représenter en mémoire la pile d'exécution du programme. Cette entité est utilisée par les deux interpréteurs. Du point de vue extérieur, les états mémoires modélisent un empilement de quadruplets <i,v,t,s> sur lequel diverses opérations sont possibles.
Un quadruplet <i,v,t,s> est composé des quatre éléments suivants : On distingue trois types d'opérations possibles sur les états mémoires :


Figure 3.1: Exemple de représentation externe


3.1.2  Choix d'implantation

Au cours du projet, deux implantations différentes ont été faites pour réaliser la structure de données. La première, réalisée à l'aide de l'objet LinkedList en Java a été rapidement développée. La seconde, plus élaborée et plus performante a nécessité plus de temps de développement. Dans un premier temps, le fait d'avoir développé une version rapidement disponible a permis aux équipes chargées des interpréteurs de commencer leur travail sans attendre la version finale. Du point de vue utilisateur, les deux implantations proposent strictement les mêmes services et fonctions. Les deux parties suivantes traitent des avantages et inconvénients de chacune des implantations.

Liste chaînée

La LinkedList est un objet Java natif qui met à disposition une structure de données sous forme de liste chaînée. Techniquement, peu de manipulations sont nécessaires pour la transformer en pile. Il suffit d'ajouter en fin de liste les éléments qu'on ajoute à la pile, et de les prendre en fin pour dépiler. Avantage : l'implantation est rapide. En effet, l'objet natif de java possède de nombreuses fonctions : Ces procédures permettent de satisfaire toutes les attentes des fonctions implantées pour la gestion des états mémoires. Inconvénients : L'inconvénient majeur résulte directement de la façon dont la structure de données est implantée. En effet, pour les fonctions d'accès à un endroit précis de la pile, le fait d'utiliser un iterator n'est pas optimal. Pour pallier ce problème, une autre implantation des états mémoires est possible, elle se base sur les tables de hachage.

Table de hachage

Principe général : dans cette configuration, les quadruplets sont communs à deux structures de données. Ils sont à la fois composants de la pile et éléments de la table de hachage. La table de hachage les indexe afin de pouvoir facilement accéder à la valeur qu'ils contiennent, et la pile permet d'avoir la structure souhaitée (Fig. 3.2).


Figure 3.2: Schéma sommaire


Caractéristiques de la table de hachage :
Gestion des conflits : Lorsque deux quadruplets possèdent le même identifiant (par exemple y  apparaît deux fois) la table de hachage doit tous les référencer, et ce pour deux raisons :
La table de hachage fonctionne de la manière suivante : une clé référence un emplacement. Si, lorsqu'on ajoute un élément dans la table, un objet est déjà dans l'emplacement pointé par la clé, on ne peut l'écraser pour mettre le nouveau. On se doit de gérer le conflit. Dans notre cas, le nouvel élément est accroché via un chaînage au bout de(s) l'élément(s) déjà présent(s). Ainsi, avec cette méthode d'ajout en détournant le conflit, l'élément le plus récent est ajouté au bout de la chaîne.


Figure 3.3: La gestion du conflit dans la table de hachage


Les deux quadruplets grisés (Fig 3.3) possèdent le même identifiant, ils sont donc chaînés à la suite depuis la table de hachage. Cependant ils ne sont pas chaînés à la suite sur la pile, car cela dépend de leur apparition dans le programme. L'implantation finale des états mémoires a été faite en utilisant ce procédé. Le chaînage des différents éléments s'est fait par référence entre objets. Pour ce qui est de la table de hachage, l'objet HashTable de Java a été utilisé. Les autres choix techniques d'implantation sont décrits dans la partie suivante.

3.2  Compte-rendu de réalisation

Les paragraphes suivants ont pour but d'expliquer l'implantation technique des états mémoires.

3.2.1  Choix techniques d'implantation

Le concept

Trois classes principales et deux annexes participent à l'implantation des états mémoires. L'imbrication de celles-ci se fait de la manière suivante : Classes principales : Classes annexes :


Figure 3.4: Interaction entre classes


Les classes

La classe Quadruplet
Cette classe permet de modéliser un quadruplet. Une instance de cette classe comporte quatre champs :
La classe ElementTableDeHachage
Les instances de cette classe ont pour objectif d'encapsuler les quadruplets dans la structure de données. A une instance de ElementTableDeHachage correspond une instance de la classe Quadruplet. Elles permettent de réaliser le chaînage dans la pile et dans la gestion de conflits de la table de hachage.


Figure 3.5: Vue d'un élément du chaînage


Une instance de la classe ElementTableDeHachage contient une instance de la classe Quadruplet (Fig. 3.5). Le double chaînage, que ce soit pour la pile ou pour la résolution des conflits de la table de hachage, a été choisi pour permettre un meilleur parcours des listes chaînées. Pour la pile, cela permet de parcourir de bas en haut ou inversement. Pour la table de hachage, cela permet un parcours aisé des éléments de même identifiant. Le but premier a été d'avoir une implantation générique de la structure de données afin de faciliter tout développement en surcouche.
La classe EtatMemoire
Cette classe correspond à la structure mère des états mémoires. Elle possède en données privées : Toutes les fonctions suivantes, qui permettent une manipulation des états mémoires sont membres (soit en privé, soit en public) de cette classe.

3.2.2  Implantation des fonctions de manipulation des structures

private void insererDansTable(ElementTableHachage elmt)
Cette fonction permet d'insérer dans la table de hachage une instance de la classe ElementTableDeHachage. Pour ce faire, l'identifiant du quadruplet encapsulé dans cette instance est récupéré. Ensuite on utilise l'identifiant comme clé de la table et on regarde si l'emplacement concerné est pris. Si c'est le cas, on résout le conflit, sinon on l'ajoute à cet endroit.
private void retirerDansTable(ElementTableHachage elmt)
Cette fonction permet de retirer un élément de la table de hachage. A partir de l'identifiant du quadruplet encapsulé on regarde si l'élément est seul à sa place, si c'est le cas on le retire simplement. Sinon on retire l'élément de la chaîne de résolution de conflits et on raccroche les deux bouts.
private void insererAvant(ElementTableHachage elmt, ElementTableHachage elmtCourant)
Cette fonction permet d'insérer un élément avant un autre dans la pile. Pour ce faire on utilise le chaînage et la connaissance du haut de la pile. De ce fait on redescend dans la pile jusqu'à trouver l'élément concerné. De plus l'élément est ajouté dans la table de hachage.
private void insererAvantEtNonDansLaTable(ElementTableHachage elmt, ElementTableHachage elmtCourant)
Cette fonction permet d'insérer un élément avant un autre dans la pile. Pour ce faire on utilise le chaînage et la connaissance du haut de la pile. De ce fait on redescend dans la pile jusqu'à trouver l'élément concerné. Cette fonction à la particularité de n'insérer les quadruplets que dans la pile et non dans la table de hachage.
private void retirerElmt(ElementTableHachage EltADepiler)
Cette fonction a pour but de retirer un élément (et donc un quadruplet) de la structure de données générale. Il est retiré d'une part du chaînage de la pile, et retiré d'autre part de la table de hachage (avec conservation du chaînage s'il y avait conflit).
private ElementTableHachage dernierInsereDansTable(String Id)
Cette fonction retourne le dernier élément inséré dans la table de hachage pour un identifiant donné. Pour ce faire, à partir d'un identifiant, on accède à l'emplacement concerné de la table, on regarde si un chaînage est présent. Si c'est la cas, on parcourt toute la liste pour aller à la fin et chercher le dernier élément. Sinon on retourne simplement le seul élément présent à l'emplacement.

3.2.3  Opérations de manipulation externe de la mémoire

Ces fonctions correspondent aux fonctions membres de la classe EtatMemoire déclarées en public. Elles correspondent pour la plupart aux spécifications des états mémoires du cours. Chacune de ces fonctions utilise soit les fonctions privées vues précédemment, soit directement les données membres. Par exemple, la fonction creer initialise chaque donnée membre directement, tandis que la fonction depiler fait appel à la fonction privée retirerElmt. Remarques : l'interfaçage avec le tas est réalisé implicitement dans les fonctions qui l'utilisent. En plus des fonctions décrites dans les spécifications, des fonctions d'interfaçage ont été développées.

3.2.4  Opérations supplémentaires

Affichage de la mémoire

Pour ce faire, deux fonctions sont possibles : toString() et toHtml(). La première produit une sortie ascii en mode texte (sous forme de chaîne de caractères) qui représente la pile et les différents quadruplets qui la composent. La seconde permet aussi une sortie en ascii mais avec un texte enrichi de balises HTML pour permettre un affichage esthétique de la pile.

Affichage de la table de hachage

L'affichage de la table de hachage est permis grâce à la chaîne de caractères retournée par la fonction hashTableToString(), contrairement aux autres, cette fonction doit être appelée explicitement depuis une instance de la classe EtatMemoire. Elle permet de représenter avant tout la gestion des conflits entre les quadruplets de même identifiant.

3.2.5  Finesses et subtilités

Un des points noirs du développement des états mémoires est la gestion du Omega. En effet, lorsqu'un quadruplet est inséré dans la mémoire (par exemple, par la fonction empiler) et que celui-ci a un identifiant de type Omega (c'est-à-dire nul), on se doit de ne pas l'insérer dans la table de hachage. Une clé nul  ne marche pas et une clé Omega  est dangereuse car si une des variables du code source s'appelle Omega, alors il y a conflit. Pour pallier ce problème, lors de l'insertion d'un quadruplet, on teste l'identifiant. Si celui-ci est nul alors on ne gère l'insertion que dans la pile (le quadruplet encapsulé n'est chaîné que dans la pile). Sinon l'insertion se fait normalement : chaînage dans la pile et ajout dans la table de hachage.

3.2.6  Gestion des erreurs

Afin d'avertir les utilisateurs (les interpréteurs) de la mauvaise utilisation des états mémoires, différentes exceptions peuvent être levées.
EtatMemoireExceptionDeclarVarIncorrect
Exception levée par la fonction DeclarVar1 si l'interpréteur tente de déclarer une variable dans la pile (en général pour un passage de paramètre) là où il n'a pas le droit. C'est-à-dire là où l'identifiant du quadruplet n'est pas nul.
EtatMemoireExceptionElementPasDansTable
Exception levée par la fonction retirerDecl si l'identifiant de quadruplet passé par l'interpréteur n'est pas connu dans les états mémoires.
EtatMemoireExceptionPileVide
Exception levée par la fonction dépiler. Intervient si l'interpréteur fait appel à la fonction dépiler alors que la pile est vide.
EtatMemoireExceptionSwapIncorrect
Exception levée par la fonction de swap si la pile de dispose pas de suffisamment d'éléments (deux) pour échanger son sommet avec l'élément précédent.

La mailbox

Les exceptions précedentes sont lancées lorsqu'un problème d'interprétation est révelé. En effet, si le fichier MiniJaja original est correct, les deux interpréteurs ne doivent pas déclancher ces exceptions. Pour ce qui est des autres erreurs possibles (pile pleine, tas plein, accès incorrect en mémoire) un système différent a été utilisé: la mailbox (boîte de messages). Si au cours de l'utilisation des états mémoires une erreur de ce genre est décelée, un message est posté dans une boite temporaire. L'IHM vient relever la boite de messages après chaque appel à l'interpréteur. Si un message est présent, l'utilisateur en est directement averti. Ce système a été choisi pour les erreurs qui doivent être transmises à l'utilisateur. En effet, les exception auraient pu être utilisés mais la gestion de leur renvoi à travers les différentes couches objets (Interpréteur, Etats Mémoires et Tas) s'est avérée plus complexe à gerer.

3.3  Scénarii de test

Détails de scénarii globaux:

3.3.1  Données nécessaires au passage des tests

Ci-dessous la liste des données nécessaires pour passer les tests

3.3.2  Références des exigences testées

  1. Bon chaînage de la pile.
  2. Bon chaînage de la gestion des conflits dans la table de hachage.
  3. Présence correcte dans la pile.
  4. Présence correcte dans la table de hachage.
  5. Respect des spécifications pour la fonction testée.

3.3.3  Détail des cas de test

Ci-dessous le détail des cas de test :

3.3.4  Analyse des résultats de test

Cette partie a été rendue possible grâce aux fonctions d'affichage en mode texte des états mémoires. En effet, un test retourne directement sous forme de fichier les états de la mémoire à des intants choisis pendant le test. Il suffit alors d'observer l'évolution de la pile d'une part et de la table de hachage d'autre part pour obtenir le verdict du test. Au cours de la campagne de test de ce module, des corrections ont été faites sur la structure de donnée et plus précisement sur le chaînage des quadruplets. En ce qui concerne les tests de charge, la mémoire a très bien supportée l'insertion d'un très grand nombre de quadruplets que ce soit en mode identique , redondant  ou différent.
Previous Up Next