Le résultat est 1. C'est un effet du type de donnée 'word' qui autorise les valeurs entières entre 0 et 65535. Si on avait aditionné 65535 et 1, on aurait trouvé 0.
L'amplitude du type étant de 65536 (65535 - 0 + 1), on effectue un modulo (le reste de la division entière) sur le vrai résultat pour avoir le résultat informatique. Ainsi 65535 + 2 = 65537 et 65537 modulo 65536 vaut 1.
Pour ceux qui connaissent la numérotation binaire (et seulement pour eux), la vraie raison de ce modulo est la suivante : une donnée de type 'word' est stockée dans l'ordinateur par 16 chiffres binaires. Ainsi, 65535 vaut 1111111111111111. Si on ajoute 2, on obtient 10000000000000001, mais qui est tronqué à 16 chiffres, soit 0000000000000001 qui vaut 1.
function VolumeCyl(RayonBase, Hauteur: Single): Single;
begin
Result := PI * RayonBase * RayonBase * Hauteur;
end;
Et c'est tout : il a suffi de recopier la formule en utilisant le paramètre RayonBase.
function PerimetreCercle(Rayon: Single): Single;
begin
result := 2 * PI * Rayon;
end;
Surface = AireDisque x 2 + PerimetreCercle x Hauteur
Les paramètres nécessaires sont la hauteur et le rayon de la base, tous deux de type Single. Le résultat de la fonction sera aussi de type Single. Voici enfin cette fonction :
function SurfaceCyl(Hauteur, RayonBase: Single): Single;
begin
result := 2 * AireDisque(RayonBase) +
PerimetreCercle(RayonBase) * Hauteur;
end;
Vous remarquerez que l'instruction, trop longue pour tenir sur une ligne dans ce guide, a été coupée en deux, à un endroit correct, c'est-à-dire à un endroit où un blanc étant possible. Il est possible de découper une instruction en plusieurs lignes, en insérant les sauts de lignes là où l'on aurait pû mettre un espace par exemple, ce qui est le cas ci-dessus.
Un bloc type se déclare toujours avec le mot réservé correspondant. Le premier type à définir est une chaîne de caractère de 200 caractères. On utilise donc la syntaxe donnée au chapitre 5 :
string[200]
Le deuxième type à définir est un équivalent du type integer. Le nouveau type sera donc une sorte de synonyme de integer. Voici le code source répondant à l'exercice :
type
TChaine200 = string[200];
TEntier = integer;
Nous appelerons TJourSemaine ce nouveau type énuméré. Les valeurs seront toutes nommées en utilisant le préfixe "js" qui permet de se rappeler, lorsqu'on en voit un, du type associé (TJourSemaine). Voici une proposition de bloc type à écrire :
type
TJourSemaine =
(jsLundi, jsMardi, jsMercredi, jsJeudi, jsVendredi, jsSamedi, jsDimanche);
(Vous aurez certainement remarqué qu'un retour à la ligne et quelques espaces ont été insérés après le signe "=" : c'est tout à fait permis à beaucoup d'endroits et ceci permet d'aèrer le code ou de permettre de faire tenir tout un texte sur une ligne, comme dans le cas présent)
Le reste de l'exercice est très semblable à ce qui est fait juste avant l'exercice dans le chapitre 5. Pour cette raison, j'y ai ajouté une petite difficulté : ne pas utiliser de variable temporaire pour stocker la valeur ordinale de "Jour". La solution est d'utiliser ce qui est affecté à cette variable temporaire à sa place. Voici donc les deux versions, avec puis sans variable intermédiaire :
procedure TForm1.Button1Click(Sender: TObject);
type
TJourSemaine =
(jsLundi, jsMardi, jsMercredi, jsJeudi, jsVendredi, jsSamedi, jsDimanche);
var
Jour: TJourSemaine;
I: Byte; { avec variable intermédiaire }
begin
Jour := jsMardi;
I := Ord(Jour);
ShowMessage(IntToStr(I));
end;
procedure TForm1.Button1Click(Sender: TObject);
type
TJourSemaine =
(jsLundi, jsMardi, jsMercredi, jsJeudi, jsVendredi, jsSamedi, jsDimanche);
var
Jour: TJourSemaine;
{ sans variable intermédiaire }
begin
Jour := jsMardi;
ShowMessage(IntToStr(Ord(Jour)));
end;
Dans notre cas, la valeur affichée sera donc la valeur ordinale de jsMardi, qui est le deuxième identificateur déclaré dans TJourSemaine, soit la valeur 1.
Le type tableau de chaînes de caractères, indexé de 1 à 10, s'écrit :
array[1..10] of string
Déclarer ce type ne doit pas poser de problème, de même que déclarer la variable de ce type. Pour initialiser une case du tableau, il faut faire suivre le nom de cette variable du numéro de la case entre crochets, ce qui permet d'utiliser la case et non le tableau. Chaque case, rappelez-vous le, est de type chaîne : on affecte donc une chaîne à cette case.
L'affichage du contenu de cette case se fait avec ShowMessage. La chaîne à afficher est dans la case initialisée auparavant. On utilise la même syntaxe que ci-dessus pour y accèder. Voici un code source répondant aux exigences de l'exercice :
procedure TForm1.Button1Click(Sender: TObject);
type
TTabChaine = array[1..10]
of string;
var
Chaines: TTabChaine;
begin
Chaines[4] := 'Bonjour';
ShowMessage(Chaines[4]);
end;
Note : vous pouvez également déclarer une variable temporaire S, lui affecter la valeur de Chaines[4], puis afficher S, mais c'est parfaitement inutile, comme le montre le code ci-dessus.
le tableau utilise en guise d'indices le type TJourSemaine (type énuméré). Le type de chaque case est string. Voici le type correspondant :
array[TJourSemaine] of string
De deux choses l'une, soit nous déclarons un nouveau type (TJourChaines par exemple) et nous utilisons TJourChaines pour déclarer la constante, soit nous déclarons directement la constante avec le type décrit ci-dessus. Pour nous entraîner à déclarer des types, nous opterons pour la première solution, plus longue.
Voici la déclaration du type et de la constante :
type
TJourSemaine =
(jsLundi, jsMardi, jsMercredi, jsJeudi, jsVendredi, jsSamedi, jsDimanche);
TJourChaines = array[TJourSemaine] of string;
const
NomsDesJours: TJourChaines =
('Lundi', 'Mardi',
'Mercredi', 'Jeudi', 'Vendredi',
'Samedi', 'Dimanche');
La constante NomsDesJours est donc un tableau de chaînes, indicées par le type TJourSemaine. Chaque chaîne est contenue dans une case indicée par une valeur correspondante de type TJourSemaine. NomsDesJours[jsLundi], par exemple, est une chaîne et vaut 'Lundi'. Avec ces explications, il devient facile de réécrire la procédure en répondant à la question 2 :
procedure TForm1.Button1Click(Sender: TObject);
type
TJourSemaine =
(jsLundi, jsMardi, jsMercredi, jsJeudi, jsVendredi, jsSamedi, jsDimanche);
TJourChaines = array[TJourSemaine] of string;
const
NomsDesJours: TJourChaines =
('Lundi', 'Mardi',
'Mercredi', 'Jeudi', 'Vendredi',
'Samedi', 'Dimanche');
var
Jour: TJourSemaine;
begin
Jour := jsMardi;
ShowMessage(NomsDesJours[Jour]);
end;
Voici ce qu'affiche le programme lorsqu'on clique sur l'habituel bouton de test :
Suivez ces étapes si vous êtes en difficulté :
var
Reponse: string;
InputQuery('Racine carrée', 'Entrez un nombre', Reponse);
Reponse := '0';
NbTest := StrToFloat(Reponse);
if X < 0 then
instruction si X < 0
else
instruction sinon (si X >= 0);
ShowMessage('Pas de racine carrée pour les nombres négatifs')
A noter qu'on ne la termine pas par un point-virgule (;) (un point-virgule terminera le bloc if qui constitue, je le rappelle, une seule instruction).
sqrt(NbTest)
On convertit directement le résultat en chaîne de caractères :
FloatToStr(sqrt(NbTest))
Et on affiche cette chaîne au milieu d'un message (ce qui constitue l'instruction complète :
ShowMessage('La racine carrée est ' + FloatToStr(sqrt(NbTest)))
On peut même améliorer cette instruction de cette manière (en indiquant le nombre et sa racine carrée dans le message) :
ShowMessage('La racine carrée de ' + FloatToStr(NbTest) + ' est ' + FloatToStr(sqrt(NbTest)))
En définitive, l'instruction contenant le bloc if est :
if NbTest < 0 then
ShowMessage('Pas de racine carrée pour les nombres négatifs')
else
ShowMessage('La racine carrée de ' + FloatToStr(NbTest) + ' est ' + FloatToStr(sqrt(NbTest)));
Voici l'une des nombreuses solutions possibles :
procedure TForm1.Button1Click(Sender: TObject);
var
Reponse: string;
NbTest: extended;
begin
{ initialisation de Reponse }
Reponse := '0';
{ demande d'un nombre }
InputQuery('Racine carrée', 'Entrez un nombre', Reponse);
{ transformation de cette réponse en nombre }
NbTest := StrToFloat(Reponse);
{ deux cas, utilisation d'un 'if' }
if NbTest < 0 then
{ nombre négatif, pas de racine carrée }
ShowMessage('Pas de racine carrée pour les nombres négatifs')
else
{ calcul et affichage de la racine carrée }
ShowMessage('La racine carrée de ' + FloatToStr(NbTest) + ' est ' + FloatToStr(sqrt(NbTest)));
end;
Lors du lancement du logiciel, un clic sur le bouton affiche ce qui suit :
Lorsqu'on rentre, par exemple, 10, voici ce qui apparaît alors :
Cet exercice est plus délicat que le précédent : il teste vos acquis sur les fonctions ainsi que sur les blocs if. Pour chacune des questions, une solution est donnée, qui est suivie de commentaires comprenant des erreurs possibles.
Concernant tout d'abord la procédure 'TForm1.Button1Click', voici le code source utilisé :
procedure TForm1.Button1Click(Sender: TObject);
var
NbTest: extended;
begin
NbTest := 0;
ShowMessage('Le nombre est ' + NbNegatif(NbTest));
end;
Dans les autres questions, seule l'appel de 'NbNegatif' changera. Cette procédure ressemble trait pour trait à d'autres que nous avons déjà étudié : une variable de type 'extended' (on n'a pas précisé quel nombre on voulait manipuler, mieux vaut dans ce cas manipuler ce qu'on a de plus large). Cette variable est initialisée (c'est cette valeur que vous devez changer pour tester les divers cas de figure).
function NbNegatif(X: extended): string;
begin
if X < 0 then
result := 'négatif'
else
result := 'positif';
end;
procedure TForm1.Button1Click(Sender: TObject);
var
NbTest: extended;
begin
NbTest := 0;
ShowMessage('Le nombre est ' + NbNegatif(NbTest));
end;
Notez bien que la fonction a été écrite avant la procédure, pour être reconnue par cette dernière sans avoir besoin d'une
déclaration dans l'interface de l'unité.
D'après le texte de la question, il va falloir faire un test sur le paramètre (que l'on notera 'X'), et réagir
différement suivant deux situations : soit le nombre est strictement négatif, soit il ne l'est pas. La fonction comporte donc une
seule instruction qui est un bloc if assez simple : le test
(X < 0) décide de quelle instruction va être exécutée. Dans chaque
situation, il nous faut donner un résultat à la fonction : ceci se fait en donnant une valeur à 'result'.
function SigneNombre(X: extended): string;
begin
if X < 0 then
result := 'négatif'
else if X > 0 then
result := 'positif'
else
result := 'zéro';
end;
procedure TForm1.Button1Click(Sender: TObject);
var
NbTest: extended;
begin
NbTest := 1;
ShowMessage('Le nombre est ' + SigneNombre(NbTest));
end;
Ici, il faut également donner une réponse différente, mais avec 3 cas possibles. On utilise donc deux blocs if imbriqués l'un dans l'autre. Le premier cas (X < 0) est la condition du premier bloc if. L'instruction correspondante au cas où (X < 0) n'est pas respecté traite donc le cas (X >= 0). Le second bloc if permet de séparer ce cas en deux, d'une part si (X > 0) et enfin d'autre part si (X = 0). Dans chaque cas, 'result' est initialisé en accord avec ce qu'on sait de X.
Indications :
Corrigé :
La première étape consiste à vérifier l'existence du fichier source et la non existence du fichier destination. Pour cela, on utilise AssignFile et exit. Result est initialisé à false avant le test pour que la fonction renvoie faux si le test échoue. A la fin de la fonction, Result est à nouveau fixé à True.
function CopyFichTexte(Src, Dest: string): boolean;
begin
result := false;
if not FileExists(Src) or FileExists(Dest)
then exit;
result := true;
end;
La deuxième étape consiste à déclarer deux variables fichiers et à écrire les trois étapes de base de leur utilisation. Le fichier source est à ouvrir en lecture seule avec Reset, tandis que le fichier destination est à ouvrir en écriture seule avec Rewrite. Voici la suite du code :
function CopyFichTexte(Src, Dest: string): boolean;
var
SrcText, DestText: TextFile;
tmpS: string;
begin
result := false;
if not FileExists(Src) or FileExists(Dest)
then exit;
AssignFile(SrcText, Src);
AssignFile(DestText, Dest);
Reset(SrcText);
Rewrite(DestText);
{ copie à programmer ici }
CloseFile(DestText);
CloseFile(SrcText);
result := true;
end;
La troisième étape consiste à faire la copie du fichier. Pour cela, on utilise une boucle while qui lit une ligne et l'écrit aussitôt. La boucle s'arrète lorsque le fichier source ne contient plus de lignes à copier. Voici le code complet de la fonction de copie :
function CopyFichTexte(Src, Dest: string): boolean;
var
SrcText, DestText: TextFile;
tmpS: string;
begin
result := false;
if not FileExists(Src) or FileExists(Dest)
then exit;
AssignFile(SrcText, Src);
AssignFile(DestText, Dest);
Reset(SrcText);
Rewrite(DestText);
while not Eof(SrcText) do
begin
Readln(SrcText, tmpS);
Writeln(DestText, tmpS);
end;
CloseFile(DestText);
CloseFile(SrcText);
result := true;
end;
La deuxième fonction à écrire, CopyFichTexteDemi, est une adaptation de la précédente destinée à vous faire manipuler une variable et à en tenir compte dans votre programmation. Nous allons utiliser une variable Cptr de type integer. Cette variable nous permettra de compter les lignes lues et de n'effectuer l'écriture que sous une condition sur Cptr. Il faudra tout d'abord penser à initialiser Cptr à 0 (0 lignes lues) avant de lancer la copie. Après chaque lecture, et avant l'écriture, il faudra incrémenter Cptr. L'écriture sera faite dans un bloc if. Les valeurs impaires de Cptr devant permettre l'écriture, la condition à vérifier sera :
Cptr mod 2 = 1
Notez que dans le code ci-dessus, les parenthèses sont inutiles car l'opérateur mod est prioritaire sur l'opérateur = . Voici la fonction :
function CopyFichTexteDemi(Src, Dest: string): boolean;
var
SrcText, DestText: TextFile;
tmpS: string;
cptr: integer;
begin
result := false;
if not FileExists(Src) or FileExists(Dest)
then exit;
AssignFile(SrcText, Src);
AssignFile(DestText, Dest);
Reset(SrcText);
Rewrite(DestText);
cptr := 0;
while not Eof(SrcText) do
begin
Readln(SrcText, tmpS);
cptr := cptr + 1;
if cptr mod 2 = 1 then
Writeln(DestText, tmpS);
end;
CloseFile(DestText);
CloseFile(SrcText);
result := true;
end;
Cet exercice est bien plus complet que tout ce que vous avez créé jusqu'à présent. Il faudra faire preuve d'organisation car le code source va avoir tendance à allonger.
Réponse aux questions :
const
NbPrenoms = 6;
TabPrenoms: array[1..NbPrenoms] of
string =
('Jean', 'Jacques', 'Marie', 'Claire', 'Pierre', 'Anne');
TabSexes: array[1..NbPrenoms] of
boolean =
(true, true, false, false, true, false);
NbNoms = 4;
TabNoms: array[1..NbNoms] of
string =
('Legrand', 'Lepetit', 'Legros', 'Leneuf');
AgeMin = 20;
AgeMax = 70;
Tout d'abord, une constante "NbPrenoms" fixe le nombre de prénoms donnés. La constante "TabPrenoms" est une constante tableau de chaînes de caractères donnant les prénoms possibles. La même chose est faite avec "NbNoms" et "TabNoms". "TabSexes" est un tableau indicé de la même manière que "TabPrenoms" : chaque case indique quel sexe est associé avec le prénom dans la case avec le même indice dans "TabPrenoms". Enfin, "AgeMin" et "AgeMax" permettent de spécifier une fourchette dans laquelle les âges doivent être générés.
Venons-en maintenant à la fonction "GenerePersonneAlea". Cette fonction utilisera une variable "tmp" de type entier pour stocker un nombre aléatoire choisi entre deux bornes. Ce nombre sera utilisé ensuite pour fixer une ou plusieurs valeurs de la personne à générer. En ce qui concerne justement cette personne, on peut utiliser directement "Result" qui est de type TPersonne. Voici la première partie de la fonction, qui choisit un nom pour la personne :
tmp := Trunc(Random(NbNoms)) + 1;
Result.Nom := TabNoms[tmp];
"tmp" est d'abord fixé à une valeur entière entre 0 et (NbNoms - 1), puis on lui ajoute 1 pour avoir une valeur entre 1 et NbNoms. Cette valeur est alors utilisée comme indice dans le tableau TabNoms et le nom contenu dans la case indicée est écrit dans le champ Nom de Result.
De même, on génére le prénom. Le sexe est déterminé en réutilisant "tmp" dans le tableau "TabSexes" :
tmp := Trunc(Random(NbPrenoms)) + 1;
Result.Prenom := TabPrenoms[tmp];
Result.Homme := TabSexes[tmp];
Enfin, l'âge est déterminé par une formule assez simple à comprendre. On génére un nombre compris entre AgeMin et AgeMax :
Result.Age := Trunc(Random(AgeMax - AgeMin + 1)) + AgeMin;
Voici donc la fonction complète :
function GenerePersonneAlea: TPersonne;
var
tmp: Integer;
begin
tmp := Trunc(Random(NbNoms)) + 1;
Result.Nom := TabNoms[tmp];
tmp := Trunc(Random(NbPrenoms)) + 1;
Result.Prenom := TabPrenoms[tmp];
Result.Homme := TabSexes[tmp];
Result.Age := Trunc(Random(AgeMax - AgeMin + 1)) + AgeMin;
end;
procedure GenereFichierSeq(NombrePersonne: Integer);
var
indx: integer;
tempF: TPersFich;
Pers: TPersonne;
begin
AssignFile(tempF, FichierTest);
if FileExists(FichierTest) then
Reset(tempF)
else
Rewrite(tempF);
Cet extrait de code déclare les variables nécessaires, et ouvre le fichier suivant qu'il existe ou non. On peut dés à présent écrire la fin de la procédure, qui fermera simplement le fichier ouvert :
CloseFile(tempF);
end;
entre les deux, il faut d'abord vider le fichier :
Seek(tempF, 0); Truncate(tempF);
Ensuite, l'écriture se fait à l'intérieur d'une boucle for : une personne est générée aléatoirement par l'appel à GenerePersonneAlea, puis cette personne est écrite dans le fichier par l'appel de la procédure "Write" :
for indx := 1 to NombrePersonne
do
begin
Pers := GenerePersonneAlea;
Write(tempF, Pers);
end;
function AffichePersonne(Pers: TPersonne): String;
const
Genres: array[boolean] of string = ('Femme', 'Homme');
begin
Result := Pers.Nom + ' ' + Pers.Prenom + ' : ' +
Genres[Pers.Homme] + ' de ' +
IntToStr(Pers.Age) + ' ans.';
end;
procedure TraiteFichierSeq(Liste: TListBox; AgeMinimum: Integer);
Le paramètre "Liste" est à considérer comme n'importe quel autre objet de classe "TListBox". Ainsi, la première étape qui consiste à vider la liste, s'écrit en appelant la méthode Clear de la propriété Items du composant "Liste" :
Liste.Items.Clear;
Ensuite, il nous faut tester l'existence du fichier de personnes : s'il n'existe pas, il est impossible d'effectuer le filtrage et on n'a d'autre choix que de quitter la procédure. Voici ce que cela donne :
if not FileExists(FichierTest) then
exit; { impossible de traiter le fichier s'il n'existe pas }
Lorsque cette étape est passée, on peut enfin passer à l'ouverture du fichier : celle-ci se fait avec un "Reset" car on est certain que le fichier existe. On se positionne, par sécurité, au tout début du fichier.
AssignFile(tempF, FichierTest);
Reset(tempF);
Seek(tempF, 0);
Une fois le fichier ouvert, on lance une boucle qui lit le fichier enregistrement par enregistrement. Cette lecture se termine lorsqu'on atteint la fin du fichier. A chaque itération, on lit un enregistrement. Si cet enregistrement correspond au critère d'affichage (âge lu >= âge minimum), on ajoute une ligne à la liste transmise. La chaîne ajoutée est obtenue en appelant la fonction "AffichePersonne" écrite à cet effet auparavant.
while not eof(tempF) do
begin
Read(tempF, Pers);
if Pers.Age >= AgeMinimum then
Liste.Items.Add(AffichePersonne(Pers));
end;
Enfin, on ferme le fichier.
CloseFile(tempF);
procedure TfmPrinc.btGenTestClick(Sender: TObject);
begin
GenereFichierSeq(100);
end;
La deuxième procédure n'est pas tellement plus compliqué à programmer. L'appel à "TraiteFichierSeq" se fait en donnant "lbResuFiltre" comme premier paramètre, et "StrToInt(edAge.Text)" comme second (on se permet ici de ne pas faire de tests, mais ce n'est pas une habitude à prendre). L'affichage du nombre de personnes ajoutées à la liste se fait en transmettant le nombre d'éléments de la zone de liste à "IntToStr" puis "ShowMessage".
procedure TfmPrinc.btFiltreClick(Sender: TObject);
begin
TraiteFichierSeq(lbResuFiltre, StrToInt(edAge.Text));
ShowMessage('Nombre de personnes : ' + IntToStr(lbResuFiltre.Items.Count));
end;
Téléchargement du projet complet : sequentiel.zip
La fonction de comptage des éléments doit d'abord vérifier que la liste ne vaut pas nil. Ensuite, si la liste est vide, aucun traitement n'est fait et la fonction se termine. Pour que la fonction puisse toujours retourner un résultat valide, on renverra -1 pour une liste incorrecte et n pour une liste contenant n élémens. Un parcours en commençant par Liste^.Dbt et en utilisant ensuite les pointeurs Suiv des maillons permet de compter les éléments. Voici le code source correspondant :
{ retourne le nombre d'éléments dans une liste }
function LCTNbElem(Liste: TListeChaineeTriee): integer;
var
Posi: PMaillon;
begin
result := -1;
if Liste = nil then exit;
result := 0;
if LCTVide(Liste) then exit;
// Posi pointe sur le premier élément, non encore compté
Posi := Liste^.Dbt;
// l'élément Posi va parcourir la liste. Tant qu'il est valide
while Posi <> nil do
begin
// on compte l'élément en cours (y compris le premier)
inc(result);
// et on passe à l'élément suivant
Posi := Posi^.Suiv;
end;
end;
La procédure de suppression d'une liste doit, après avoir testé la validité de la liste, réaliser dans tâches dans le bon ordre :
Pour cela, on fait appel à LCTSupprIdx en supprimant toujours le premier élément, ce qui est avantageux connaissant la manière dont opére LCTSupprIdx (notez qu'à ce niveau, on a le droit de tenir compte du code source des autres opérations). Voici un code source possible :
{ destruction complète d'une liste }
procedure LCTDetruire(Liste: TListeChaineeTriee);
begin
if Liste = nil then exit;
// destruction des maillons
while not LCTVide(Liste) do
Liste := LCTSupprIdx(Liste, 0);
// libération de la mémoire associée à la liste
Dispose(Liste);
end;
Une boucle supprime le premier élément tant que la liste n'est pas vide. Une fois cette condition réalisée, la mémoire associée à la liste est libérée. Quant à la procéure d'affichage du contenu de la liste, elle se contente, après initialisation de la sortie, et vérification de la liste, de réaliser un parcours en affichant chaque personne présente dans la liste.
{ affichage du contenu d'une liste }
procedure AfficheListe(Liste: TListeChaineeTriee; Sortie: TStrings);
var
Posi: PMaillon;
begin
Sortie.Clear;
if Liste = nil then exit;
// Si la liste est vide
if Liste^.Dbt = nil then
Sortie.Add('(liste vide)')
else
begin
// initialisation du parcours de la liste
Posi := Liste^.Dbt;
repeat
// pour chaque élément trouvé, affichage
Sortie.Add(AffichPers(Posi^.Elem));
// et passage à l'élément suivant
Posi := Posi^.Suiv;
// on s'arrète dés que Posi devient invalide
until Posi = nil;
end;
end;