• Aucun résultat trouvé

Les listes

2.4 EXEMPLES D’APPLICATION

2.4.5 Les piles

et de remplacer :

ptc->numero par (int)ptc ptc doit être considéré comme un entier Exercice 8 - Systèmes experts : les algorithmes de déduction

• Écrire la fonction : booleen existe (ListeFaits* listF, int num) ; qui indique si le fait num existe dans la liste listF.

• Écrire la fonction : int appliquer (Regle* regle, ListeFaits* listF) ; qui vérifie si la règle pointée par regle s’applique, et ajoute les conclusions de cette règle à la liste de faits listF si les hypothèses de la règle sont vérifiées.

• Écrire la fonction : void chainageAvant (ListeRegles* listR, ListeFaits* listF) ; qui à partir de la liste de faits listF et de la liste des règles listR, ajoute à listF les conclu-sions des règles vérifiées.

• Écrire la fonction récursive : booleen demontrerFait (ListeRegles* listR, ListeFaits*

listF, int num, int nb) ; qui en utilisant la liste de faits listF et la liste des règles listR, démontre le fait num ; nb est utilisé pour faire une indentation (au fil des appels récursifs) comme sur le schéma de la Figure 28.

• Écrire le programme principal qui crée les structures de données de la Figure 27, liste les faits et les règles, effectue le chaînage avant et le chaînage arrière.

2.4.5 Les piles

Une pile est une structure de données telle que :

• l’ajout d’un élément se fait au sommet de la pile,

• la suppression d’un élément se fait également au sommet de la pile.

La structure de données est appelée LIFO : "last in, first out" soit "dernier entré, premier sorti".

2.4.5.a Allocation dynamique (utilisation de listes)

Figure 31 Principe d’une pile gérée à l’aide d’une liste.

À l’aide d’une liste, les opérations sur une pile peuvent être réalisées comme suit : initialiserPile initialiser une liste vide

pileVide vrai si la liste est vide

empiler ajouter un élément en tête de la liste dépiler enlever un élément en tête de la liste

b c /

premier a

03Chap_02 Page 66 Samedi, 17. janvier 2004 10:36 10

2.4 • Exemples d’application 67

© Dunod – La photocopie non autorisée est un délit.

Le module de gestion de piles peut facilement se réaliser avec le module de gestion des listes présenté précédemment en utilisant insererEnTeteDeListe() et extraireEnTeteDeListe(). Un élément de la liste référence un objet spécifique de l’application. Les déclarations et les opérations sur la pile sont données ci-dessous.

2.4.5.b Le fichier d’en-tête des piles (utilisation de listes)

pile.h contient la déclaration du type pile et les prototypes des fonctions de gestion de la pile.

// pile.h pile en allocation dynamique avec des listes

#ifndef PILE_H

#define PILE_H

#include "liste.h"

typedef Liste Pile;

Pile* creerPile ();

booleen pileVide (Pile* p);

void empiler (Pile* p, Objet* objet);

Objet* depiler (Pile* p);

void listerPile (Pile* p, void (*f) (Objet*));

void detruirePile (Pile* p);

#endif

2.4.5.c Le module des piles (utilisation de listes)

pile.cpp contient le corps des fonctions dont le prototype est défini dans pile.h.

/* pile.cpp pile gérée à l'aide d'une liste */

#include <stdio.h>

#include <stdlib.h>

#include "pile.h"

// créer et initialiser une pile Pile* creerPile () {

return creerListe ();

}

// vrai si la pile est vide, faux sinon booleen pileVide (Pile* p) {

return listeVide (p);

}

// empiler objet dans la pile p void empiler (Pile* p, Objet* objet) { insererEnTeteDeListe (p, objet);

}

// fournir l'adresse de l'objet en sommet de pile, // ou NULL si la pile est vide

Objet* depiler (Pile* p) { if (pileVide (p)) { 03Chap_02 Page 67 Samedi, 17. janvier 2004 10:36 10

68 2 Les listes

return NULL;

} else {

return extraireEnTeteDeListe (p);

} }

// Lister la pile du sommet vers la base void listerPile (Pile* p, void (*f) (Objet*)) { listerListe (p, f);

}

void detruirePile (Pile* p) { detruireListe (p);

}

2.4.5.d Déclaration des types Entier et Reel pour le test de la pile

Les types Entier et Reel sont utilisés à plusieurs reprises dans la suite de ce livre. Les déclarations et le corps des fonctions traitant de ces types sont insérés dans les fichiers mdtypes.h et mdtypes.cpp (voir § 2.4.1, page 51), après les déclarations du type Personne.

Dans mdtypes.h :

// **** une structure contenant un entier typedef struct {

int valeur;

} Entier;

Entier* creerEntier (int valeur);

Entier* entier (int valeur);

void ecrireEntier (Objet* objet);

char* toStringEntier (Objet* objet);

int comparerEntier (Objet* objet1, Objet* objet2);

int comparerEntierCar (Objet* objet1, Objet* objet2);

// **** une structure contenant un réel double typedef struct {

double valeur;

} Reel;

Reel* creerReel (double valeur);

void ecrireReel (Objet* objet);

int comparerReel (Objet* objet1, Objet* objet2);

Dans mdtypes.cpp :

// **** une structure contenant un entier Entier* creerEntier (int valeur) {

Entier* entier = new Entier();

entier->valeur = valeur;

return entier;

}

void ecrireEntier (Objet* objet) { Entier* entier = (Entier*) objet;

printf ("%d\n", entier->valeur);

}

03Chap_02 Page 68 Samedi, 17. janvier 2004 10:36 10

2.4 • Exemples d’application 69

© Dunod – La photocopie non autorisée est un délit.

// constructeur de Entier Entier* entier (int valeur) { return creerEntier (valeur);

}

char* toStringEntier (Objet* objet) { char* nombre = (char*) malloc (50);

sprintf (nombre, "%d", ((Entier*)objet)->valeur);

return nombre;

}

// comparer deux entiers

// fournit <0 si e1 < e2; 0 si e1=e2; >0 sinon int comparerEntier (Objet* objet1, Objet* objet2) { Entier* e1 = (Entier*) objet1;

Entier* e2 = (Entier*) objet2;

if (e1->valeur < e2->valeur) { return -1;

// comparer des chaînes de caractères correspondant à des entiers // 9 < 100 (mais pas en ascii)

int comparerEntierCar (Objet* objet1, Objet* objet2) { long a = atoi ((char*) objet1);

// **** une structure contenant un réel double Reel* creerReel (double valeur) {

Reel* reel = new Reel();

reel->valeur = valeur;

return reel;

}

void ecrireReel (Objet* objet) { Reel* reel = (Reel*) objet;

printf ("%.2f\n", reel->valeur);

}

2.4.5.e Utilisation du module de gestion de piles

Le programme suivant définit un menu et un programme principal permettant d’initialiser une pile, de tester si la pile est vide, d’ajouter ou de retirer des éléments

03Chap_02 Page 69 Samedi, 17. janvier 2004 10:36 10

70 2 Les listes

en sommet de pile et de lister pour vérification, le contenu de la pile. Le module pile.h peut être utilisé pour n’importe quel objet à empiler (entier, réel, personne, etc.). Pour cet exemple, repris dans l’exercice suivant, la variable de compilation PILETABLEAU n’est pas définie.

// pppile.cpp programme principal des piles (avec listes ou tableau)

#include <stdio.h>

printf ("\n\nGESTION D'UNE PILE D'ENTIERS\n\n");

printf ("0 - Fin\n");

printf ("1 - Initialisation de la pile\n");

printf ("2 - La pile est-elle vide\n");

printf ("3 - Insertion dans la pile\n");

printf ("4 - Retrait de la pile\n");

printf ("5 - Listage de la pile\n");

printf ("\n");

printf ("Votre choix ? ");

int cod; scanf ("%d", &cod); getchar();

printf ("\n");

03Chap_02 Page 70 Samedi, 17. janvier 2004 10:36 10

2.4 • Exemples d’application 71

© Dunod – La photocopie non autorisée est un délit.

case 2:

listerPile (pile1, ecrireEntier);

break;

} // switch }

detruirePile (pile1);

printf ("\n\nGESTION D'UNE PILE DE PERSONNES\n");

#if PILETABLEAU

printf ("avec un tableau de %d places\n", LGMAX);

Pile* pile2 = creerPile(LGMAX);

#else

Pile* pile2 = creerPile();

#endif

empiler (pile2, creerPersonne("Dupont", "Jacques"));

empiler (pile2, creerPersonne("Dufour", "Jacques"));

empiler (pile2, creerPersonne("Dupré", "Jacques"));

empiler (pile2, creerPersonne("Dumoulin", "Jacques"));

printf ("Valeurs dans la pile : du sommet vers la base\n");

listerPile (pile2, ecrirePersonne);

printf ("\nValeur dépilée : ");

Personne* p = (Personne*) depiler (pile2);

if (p!=NULL) ecrirePersonne (p);

printf ("\n\nGESTION D'UNE PILE DE REELS\n");

#if PILETABLEAU

printf ("avec un tableau de %d places\n", LGMAX);

Pile* pile3 = creerPile(7);

#else

Pile* pile3 = creerPile();

#endif

empiler (pile3, creerReel (2.5));

empiler (pile3, creerReel (3.5));

03Chap_02 Page 71 Samedi, 17. janvier 2004 10:36 10

72 2 Les listes

empiler (pile3, creerReel (5.5));

printf ("Valeurs dans la pile : du sommet vers la base\n");

listerPile (pile3, ecrireReel);

printf ("\nvaleur dépilée : ");

Reel* r = (Reel*) depiler (pile3);

if (r!=NULL) ecrireReel (r);

}

2.4.5.f Allocation contiguë (utilisation d’un tableau)

Les piles peuvent être également gérées à l’aide d’un tableau alloué sur des cases contiguës et de taille a priori connue et donc limitée (à 7 sur la Figure 32). Les éléments sont consécutifs en mémoire. Chaque élément du tableau contient un poin-teur sur un objet de la pile. La pile peut être une pile d’entiers, de réels, de personnes comme précédemment.

Figure 32 Principe d’une pile gérée à l’aide d’un tableau.

Exercice 9 - Le type pile (allocation contiguë)

Reprendre la déclaration de pile.h du § 2.4.5.b, page 67 et le module pile.cpp correspondant de façon à gérer la pile à l’aide d’un tableau. Tester le programme utilisateur pppile.cpp utilisé ci-dessus pour l’allocation dynamique en liste qui doit rester inchangé sauf pour creerPile() qui contient un paramètre indiquant la taille de la pile dans le cas de l’allocation contiguë. Le type pile est un TAD (Type Abstrait de Données) ; son implémentation ne doit pas affecter les programmes utilisateurs.

2.4.6 Les files d’attente (gérée à l’aide d’une liste)