Vous êtes sur la page 1sur 40

Deuxime partie : Cours de I

re

Applications Delphi

page 48 de 87

page 49 de 87

7 Delphi
7.1 Introduction
Aprs son lancement, Delphi se prsente sous la forme de 4 fentres. La premire fentre occupe la partie suprieure de l'cran. Elle correspond l'environnement de programmation proprement dit. Cette fentre contient : la barre de titre ; la barre de menu de Delphi ; une zone barre d'outils (sur la gauche) ; une zone contenant les divers composants regroups par familles.

La seconde fentre se trouve par dfaut gauche de l'cran : c'est l'inspecteur d'objets. Il permet de visualiser, pour chaque objet ou composant, les proprits et les vnements auxquels l'objet peut rpondre. La troisime fentre constitue la fiche principale de la future application Delphi. Il s'agit, au dpart, d'une fentre vide dans laquelle on placera les divers objets. La dernire fentre, cache sous la prcdente constitue lditeur proprement dit, contenant le code source de l'application. Pour dmarrer une nouvelle application, il faut choisir l'option New Application du menu File. Pour sauvegarder une application, il faut choisir l'option Save All du menu File. Une rgle suivre absolument est de crer un rpertoire par application. Comme Delphi cre plusieurs fichiers pour une application donne, il est plus facile de les retrouver s'ils ne sont pas enregistrs avec d'autres fichiers de noms pratiquement identiques. Lors du premier tout enregistrement de l'application, une fentre permet de choisir l'emplacement de sauvegarde et mme de le crer. Pour excuter une application, il faut choisir l'option Run du menu Run. Si les options d'autoenregistrement ont t slectionnes et que l'application n'a encore jamais t sauvegarde, la fentre d'enregistrement s'affiche. L'application est ensuite compile puis excute, si elle ne contient pas d'erreur.

7.2 Les fichiers utiliss en Delphi


Les fichiers d'un projet : .DPR .DFM .PAS .EXE .DCU .RES .DPL .DPK fichier projet fichier fiche fichier unit - code source fichier excutable (le programme dvelopp) fichier unit - code compil fichier ressource (icnes, bitmaps, curseurs, . . .) fichier paquet compil fichier paquet source page 50 de 87 Delphi Package Library Delphi Package Delphi Compiled Unit Delphi Project File Delphi Form File

Les fichiers .DPR, .DFM et .PAS sont les fichiers ncessaires la programmation et doivent tre copis pour continuer le dveloppement sur une autre machine. Autres fichiers : .DOF .DSK .~?? options du projet paramtres du bureau fichiers de sauvegarde Delphi Options File Delphi Desktop File

7.3 Lapproche Oriente-Objet


Dans la programmation en Delphi, nous allons manipuler des objets. Ces objets sont dfinis par leurs proprits, leurs mthodes et leurs vnements. Dans la vie courante, un objet peut tre toute chose vivante ou non (par exemple : une voiture, une montre, ). En informatique, un objet est souvent un bouton, une fentre, un menu,

7.3.1

Les proprits

Cependant, chaque personne voit lobjet diffremment. Par exemple chacun aura une perception diffrente de lobjet voiture, selon limportance quil attribue aux caractristiques de lobjet. Une proprit est une information dcrivant une caractristique de lobjet. Ainsi, il est facile dnumrer quelques proprits pour lobjet voiture : vitesse maximale, cylindre, marque, modle, couleur, Nous pouvons consulter les proprits et galement les modifier. Par exemple, nous pouvons dfinir les proprits dune voiture dans un jeu de course, tel que la couleur. Ceci se fait de la manire suivante : Voiture1.Couleur := Rouge Bien entendu, il faut que la constante Rouge soit dfinie. Les objets (dans Delphi ces objets sont appels composants) que nous allons utiliser sont prdfinis (boutons, fentres, menus, ). Pour afficher les proprits dun objet, il suffit de cliquer dessus. Les proprits saffichent alors dans linspecteur dobjet. Il existe des composants en lecture seule.

7.3.2

Les mthodes

Pour simplifier, on peut se reprsenter une mthode comme un ordre du style fais ceci . Cet ordre provoque lexcution dune certaine action par lobjet. Par exemple, pour lobjet voiture, on peut numrer les mthodes suivantes : acclrer, freiner, changer de vitesse, Donc, linstruction Voiture1.Acclrer(10) indique la voiture quelle doit acclrer dun facteur 10. Les proprits ne font que changer une caractristique dun objet alors que les mthodes effectuent une action. On nutilise pas de signe daffectation lorsquon excute une mthode.

page 51 de 87

7.3.3

Les vnements

Pour chaque objet, il peut survenir certains vnements, qui dclenchent des ractions. Dans lexemple de la voiture, lorsquon tourne la cl dans le contact ou lorsquon appuie sur lacclrateur, la voiture respectivement dmarre ou acclre. Pour les objets informatiques il leur arrive des vnements auxquels ils peuvent ragir. Par exemple, un bouton peut avoir les vnements OnMouse (vnements lis la souris), OnKey (vnements lis au clavier), OnEnter (rception du focus), On Exit (perte du focus), Les vnements existants pour un objet sont visibles dans linspecteur dobjet.

7.4 Passage Pascal Delphi un premier exemple


7.4.1 L'interface
En Delphi, nous utiliserons les composants de l'interface graphique (les fentres de Windows) pour entrer les donnes et afficher les rsultats. Les algorithmes PASCAL que nous avons utiliss jusqu maintenant pour obtenir les rsultats pourront rester inchangs. D'abord, nous allons crer l'interface du programme. Nous allons adapter les noms internes (proprit Name) de chaque composant que nous utilisons. En plus, nous allons modifier les inscriptions sur les diffrents composants (proprits Caption ou Text). Fiche (TForm) Name: frmMain

Etiquette (TLabel) Name: lblMoy Caption: 0

Bote d'dition (TEdit) Name: edtA

Bote d'dition (TEdit) Name: edtB

Bouton (TButton) Name: btnCalcul Caption: Moyenne

page 52 de 87

7.4.2

Les conversions de types

Les donnes inscrites dans les botes ddition sont de type texte (string). Nous devons donc les transformer afin de pouvoir effectuer des calculs. Voici quelques fonctions permettant deffectuer certaines conversions : StrToInt(string) : convertit une chane de caractres en un nombre entier (type integer) StrToFloat(string) : convertit une chane de caractres en un nombre rel (type real). De mme, pour pouvoir afficher le rsultat, nous devons le transformer en texte. Ceci peut se faire grce aux fonctions FloatToStr et IntToStr.

7.4.3

Le traitement

Aprs la saisie des donnes dans les botes d'dition, l'utilisateur va cliquer sur le bouton btnCalcul. cet instant l'vnement OnClick du bouton est gnr et la mthode btnCalculClick est lance. Nous allons donc entrer les instructions effectuer dans la mthode btnCalculClick : procedure TfrmMain.btnCalculClick(Sender: TObject); var A,B,MOY : real; begin A := StrToFloat(edtA.Text); B := StrToFloat(edtB.Text); MOY := (A+B)/2; lblMoy.Caption := FloatToStr(MOY); end;

7.4.4

Exercices

Exercice 7-1 Ecrivez un programme qui affiche le plus grand de trois nombres rels A, B, C. Exercice 7-2 Ecrivez un programme qui calcule la somme d'une srie de nombres entrs au clavier, en utilisant deux botes ddition et un bouton pour la remise zro de la somme. Exercice 7-3 Ralisez le programme PUISSANCE qui calcule et affiche la puissance XN (puissance X exposant N pour un rel X et un entier N positif, ngatif ou zro). Pour les cas o XN ne se laisse pas calculer, affichez un message d'erreur ! Exercice 7-4 a) Ralisez un programme qui permet de simplifier une fraction. b) Utilisez une partie du programme ralis sous a) pour faire un programme qui additionne deux fractions.

page 53 de 87

7.5 Calcul de la factorielle


Comme premier programme essayons dimplmenter en Delphi le calcul de la factorielle. 1 K x si x 1 Rappelons que pour tout entier naturel x, x!= si x = 0 1 Pour laborer ce calcul nous pouvons utiliser le programme dvelopp dans le cours de 2e et lincorporer dans celui en Delphi.

7.5.1

Prsentation visuelle

Commenons par tablir un formulaire dans lequel nous notons les valeurs et ditons les rsultats. Voici un exemple dun tel formulaire. lblTitre lblEgal edtNombre lblResultat

btnOk

btnExit

Ce formulaire est un nouvel objet que nous appellerons Tformulaire, de capture (Caption) : Factorielle (algorithme itratif). Il est compos des proprits suivantes :

Name lblTitre

type TLabel

text

Caption Calcul de la factorielle dun nombre !=

lblEgal edtNombre

TLabel TEdit

valeur de la factorielle calculer

lblResultat
btnOk btnExit

TLabel TButton TButton Calcul Sortir

Il est vident que tous ces champs possdent encore davantage de proprits. Nous navons numr ici que les plus importantes.

page 54 de 87

7.5.2

Code

Une fois ce formulaire tabli, nous pouvons crire le code ncessaire pour calculer la factorielle. Rappelons que nous y utiliserons le code Pascal tabli en 2e (voir galement les Algorithmes obligatoires ). Delphi soccupera de la dclaration du formulaire et de ses proprits. La seule partie du code que nous devons crire est celle de la procdure btnOkClick qui va tre excute, comme son nom le dit, aprs que lutilisateur ait pouss sur le bouton Ok. Nous dirons que la procdure sexcute aprs lvnement onClick appliqu la proprit btnOk. Le tout se trouvera dans lunit Unit1. Voici une possibilit de code pour la procdure en question. procedure Tformulaire.btnOkClick(Sender: TObject); var n,fact:integer; begin n:=StrToInt(edtnum.Text); fact:=factorielle(n); lblresult.Caption:=IntToStr(fact) end;

Bien entendu, cette procdure suppose que la fonction factorielle(n:integer):integer est dfinie.

7.5.3

Explication du programme.

En regardant de prs ce code quelques remarques simposent : Comme la procdure semploie dans le formulaire Tformulaire, elle sappellera sous son nom complet : Tformulaire.btnOkClick. La valeur saisie du nombre est la valeur de la proprit Text du champ edtNombre. Nous notons donc cette valeur par edtNombre.Text. De plus, comme il sagit dune chane de caractres, nous devons encore transformer cette chane en une valeur numrique par la fonction StrToInt, fonction prdfinie dans Delphi. La valeur de la factorielle calcule sera affecte la proprit Caption du champ lblResultat que nous noterons par lblResultat.Caption. Comme de plus cette valeur doit tre du type chane de caractres, nous devons transformer fact par la fonction IntToStr, autre fonction prdfinie dans Delphi. Lunit Unit1 se prsentera finalement ainsi : unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Controls, Forms, Dialogs, StdCtrls; type Tformulaire = class(TForm) lblTitre: TLabel; page 55 de 87

Classes,

Graphics,

edtNombre: TEdit; lblEgal: TLabel; btnOk: TButton; lblResultat: TLabel; procedure btnOkClick(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var formul: Tformulaire; implementation {$R *.dfm} //fonction permettant de calculer une factorielle function factorielle(n:integer):integer; var fact:integer; begin fact:=1; while n>1 do begin fact:=fact*n; n:=n-1 end; result:=fact end; procedure TFormulaire.btnOKClick(Sender: TObject); var n,fact:integer; begin n:=StrToInt(edtnum.Text); fact:=factorielle(n); lblresult.Caption:=IntToStr(fact) end; procedure TFormulaire.btnexitClick(Sender: TObject); begin Application.Terminate; end; end.

7.5.4

Excution du programme

Une fois ce code saisi, nous sauvegardons le tout dans un rpertoire rserv cette application. Nous cliquons ensuite sur le bouton qui reprsente un petit triangle vert et le programme sexcutera.

page 56 de 87

7.5.5

Remarques

La mthode Tformulaire.btnExitClick aura comme seule commande Application.Terminate. De cette manire lvnement Click li au bouton btnExit aura comme effet net darrter lapplication. La saisie fautive respectivement dun nombre ngatif ou dcimal ne conduira pas un message derreur de la part du programme mais nous affichera un rsultat erron. Nous laissons au lecteur le soin de corriger le programme pour lamliorer de ce point de vue.

7.6 Equation du second degr


crivons maintenant un programme qui demande la saisie les trois coefficients a, b et c dune quation du second degr et qui calcule, si elles existent, les racines de lquation ax 2 + bx + c = 0 .
Ce mme programme a t demand comme exercice dans le cours de 2e. Nous en faisons ici un programme Delphi.

7.6.1

Prsentation visuelle

Comme dans lexemple prcdent nous commenons par dessiner un formulaire que nous appellerons Tformulaire, dont linstance formulaire nous permet de saisir les coefficients et de lire le(s) rsultat(s). La proprit Caption de lobjet Tformulaire aura comme valeur : quation du 2e degr.

Ce formulaire est compos des champs suivants :


name lblA lblB lblC lblTexte lblX1 lblX2 edtA type TLabel TLabel TLabel TLabel TLabel TLabel TEdit text caption a= b= c=

texte sur le rsultat valeur de la racine valeur de la racine valeur de a

page 57 de 87

edtB edtC btnCalcul gbCoeff gbRes btnExit

TEdit TEdit TButton TGroupBox TGroupBox TButton

valeur de b valeur de c
Calcul Coefficients Rsultat Sortir

Nous remarquons tout-de-suite une nouvelle notion : les champs du type TGroupBox : Ils servent regrouper diffrents champs dans un mme groupe quils affichent avec un cadre et un nom donn sous Caption. Dans notre exemple la TGroupBox gbCoeff regroupe les champs lblA, lblB et lblC, tandis la TGroupBox gbRes affichera les rsultats et contient ainsi les champs lblTexte, lblX1 et lblX2. Nous dfinissons ces TGroupBox comme suit :

Nous venons dj de remarquer que le rsultat sera affich dans la TGroupBox gbRes. Mais les tiquettes (labels) lblTexte, lblX1 et lblX2 sont invisibles pour linstant. Si nous avons un rsultat afficher, lblTexte contiendra une des phrases suivantes : Il n'y a pas de solution relle ! , Il existe une solution relle ! ou Il y a deux solutions relles diffrentes ! (ceci en fonction du rsultat du calcul), tandis que lblX1 et lblX2 contiendront les valeurs des racines ventuelles. Comme actuellement ces tiquettes ne contiennent pas de texte (caption vide), elles napparatront pas lcran.

7.6.2

Code

Une fois ce formulaire tabli nous crivons le code suivant :

page 58 de 87

unit Unit1; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons; type Tformulaire = class(TForm) gbCoeff: TGroupBox; lblA: TLabel; lblB: TLabel; lblC: TLabel; edtA: TEdit; edtB: TEdit; edtC: TEdit; btnCalcul: TButton; gbRes: TGroupBox; lblTexte: TLabel; lblX1: TLabel; lblX2: TLabel; btnExit: TButton; procedure btnCalculClick(Sender: TObject); procedure btnExitClick(Sender: TObject); private { Private-Declarations} public { Public-Declarations } end; //Tformulaire var formulaire: Tformulaire; implementation {$R *.DFM} procedure Tformulaire.btnCalculClick(Sender: TObject); var a,b,c,disc : real; begin a:=StrtoFloat(edta.Text); b:=StrtoFloat(edtb.Text); c:=StrtoFloat(edtc.Text); disc:=b*b-4*a*c; if disc < 0 then begin lblTexte.Caption:='Il n''y a pas de solution relle !'; lblX1.Caption:=''; lblX2.Caption:=''; end else if round(disc*1000000) = 0 then //un nombre reel nest jamais 0 begin lblTexte.Caption:='Il existe une solution relle !'; lblX1.Caption:='x = ' + FloattoStr(-b/(2*a)); lblX2.Caption:=''; end else if disc > 0 then begin lblTexte.Caption:='Il y a deux solutions relles diffrentes !'; lblX1.Caption:='x1 = ' + FloattoStr((-b-sqrt(disc))/(2*a));
page 59 de 87

lblX2.Caption:='x2 = ' + FloattoStr((-b+sqrt(disc))/(2*a)); end; end; procedure Tformulaire.btnExitClick(Sender: TObject); begin Application.Terminate; end; end.

7.6.3

Explications du programme

Nous lisons dabord les 3 coefficients a, b et c de lquation. Comme nous les avons dfinis comme variables relles, nous devons utiliser la fonction StrtoFloat pour faire la transformation entre la chane de caractres que reprsente edt*.Ttext et les variables a, b et c. Nous calculons ensuite le discriminant. En fonction du signe du discriminant nous envisageons les 3 cas : disc<0 : il ny a pas de rsultat rel ; disc=0 : il y a une racine ; disc>0 : il y a deux racines relles distinctes. En fonction des diffrents cas nous calculons les valeurs des racines. la fin il nous reste encore transformer les valeurs relles, rsultats des calculs, en chanes de caractres pour les affecter la proprit Caption des diffrentes tiquettes. Nous faisons ceci avec la fonction FloatToStr. La mthode Tformulaire.btnExitClick aura comme seule commande Application. Terminate. De cette manire lvnement Click li au bouton btnExit aura comme effet-net darrter lapplication.

7.7 Vrification du numro de matricule


Dveloppons ici un exercice qui prend en entre le numro de matricule dune personne et qui vrifie que le chiffre de contrle est correct.

7.7.1

Rappelons la mthode de calcul du numro de contrle.

Si nous notons a1a 2 a3 a 4 m1m2 j1 j 2 n1n2 c un numro de matricule, nous formons le dernier chiffre en parcourant les tapes suivantes : Nous formons la somme : sum = 5 * a1 + 4 * a 2 + 3 * a3 + 2 * a 4 + 7 * m1 + 6 * m2 + 5 * j1 + 4 * j 2 + 3 * n1 + 2 * n2 ; soit n le reste de la division de sum par 11 ; si n=1 il y a une faute ; si n=0 le chiffre de contrle reste 0 ; si n 0 et n 1 alors le chiffre de contrle vaut 11 n .

page 60 de 87

Nous essayons de traduire ceci en Delphi.

7.7.2

Prsentation visuelle

Pour cela il nous faut dabord un formulaire que nous appellerons, comme toujours, Tformulaire dont une instance nous servira manipuler les entres et sorties. La valeur de la proprit Caption de lobjet Tformulaire sera : Numro de matricule. Voici un exemple dun tel formulaire.

Il contient les lments suivants :

nom lblTitre

type TLabel

text

caption Contrle du numro de matricule Indiquez votre numro de matricule : Votre numro de matricule est :

lblSaisie

TLabel

lblResultat

TLabel

edtSaisie edtResultat btnVerif btnExit

TEdit TEdit TButton TButton

numro de matricule correct/faux


Vrification Sortie

page 61 de 87

7.7.3

Code

Une fois ce formulaire tabli, nous devons programmer le code ncessaire. Voici un exemple dimplmentation.

unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Controls, Forms, Dialogs, StdCtrls; type TfrmMatricule = class(TForm) lblTitre: TLabel; lblSaisie: TLabel; lblResultat: TLabel; edtSaisie: TEdit; edtResultat: TEdit; btnVerif: TButton; lblResultat: TLabel; btnExit: TButton; procedure btnVerifClick(Sender: TObject); procedure btnExitClick(Sender: TObject); private { Private declarations } public { Public declarations } end; //Tformulaire var frmMatricule: TfrmMatricule;

Classes,

Graphics,

implementation {$R *.dfm} procedure TfrmMatricule.btnVerifClick(Sender: TObject); var a1,a2,a3,a4,m1,m2,j1,j2,n1,n2,nc,c: integer; sum : integer; s : string; begin s:=edtSaisie.Text; if length(s) <> 11 then ShowMessage('Numro de matricule mal saisi') else begin a1:= StrToInt(copy(s,1,1)); a2:= StrToInt(copy(s,2,1)); a3:= StrToInt(copy(s,3,1)); a4:= StrToInt(copy(s,4,1)); m1:= StrToInt(copy(s,5,1)); m2:= StrToInt(copy(s,6,1)); j1:= StrToInt(copy(s,7,1));
page 62 de 87

j2:= StrToInt(copy(s,8,1)); n1:= StrToInt(copy(s,9,1)); n2:= StrToInt(copy(s,10,1)); nc:= StrToInt(copy(s,11,1)); sum := 5*a1+4*a2+3*a3+2*a4+7*m1+6*m2+5*j1+4*j2+3*n1+2*n2; sum := sum mod 11; if sum = 1 then s:='faux' else begin if sum = 0 then c:= 0 else c:= 11-sum; if nc=c then s:='correct' else s:='faux'; end; edtResultat.Text:=s; end; end; procedure TfrmMatricule.btnExitClick(Sender: TObject); begin Application.Terminate; end; end.

7.7.4

Explication du code

La variable s va contenir le numro de matricule saisi. Cest la valeur saisie. Pour viter quun utilisateur ne donne quune partie dun numro de matricule, nous faisons un test sur la longueur du numro et nous affichons une erreur si la longueur ne correspond pas. Le message derreur est affich par la procdure ShowMessage dont la syntaxe est la suivante :

ShowMessage(msg: string);
o msg chane de caractres afficher. Ensuite nous extrayons les diffrentes valeurs du numro de matricule. Comme toutes les valeurs saisies sont des caractres nous devons les transformer en entiers par la fonction StrToInt. La fonction copy sert extraire des parties de chanes de caractres. Sa syntaxe est la suivante :

Copy(s, index, count: Integer): string;


o :
S Index Count chane de laquelle est extraite la partie ; indice de dbut de la chane extraire (commence par 1) ; nombre de caractres extraire.

Les tapes suivantes correspondent lalgorithme nonc. Pour terminer nous ditons le rsultat comme texte du champ edtResultat. La procdure Tformulaire.btnExitClick sert de nouveau arrter lapplication. page 63 de 87

7.8 Une petite machine calculer


Dans ce prochain exercice nous nous proposons de mettre en uvre une petite machine calculer, qui effectuera les 4 oprations lmentaires.

7.8.1

Prsentation visuelle

Comme toujours il nous faut dfinir dabord un formulaire. Voici une possibilit dune telle interface entre loprateur et la machine.

Nous appellerons, comme toujours Tformulaire lobjet que nous allons dfinir ci-dessous.
Nom edtNum type TEdit text Saisie des nombres et des oprateurs qui interviennent dans le calcul 0 1 2 3 4 5 6 7 8 9 C / = caption

btnButton0 btnButton1 btnButton2 btnButton3 btnButton4 btnButton5 btnButton6 btnButton7 btnButton8 btnButton9 btnButtonclear btnButtondiv btnButtonequal btnButtonminus

TButton TButton TButton TButton TButton TButton TButton TButton TButton TButton TButton TButton TButton TButton

page 64 de 87

btnButtonmult btnButtonplus btnButtonarret

TButton TButton TButton

* + Stop

7.8.2

Code

Une possibilit de code pour cette machine calculer est le suivant :

unit Unit1; interface uses Windows, Messages, SysUtils, Controls, Forms,Dialogs, StdCtrls;

Variants,

Classes,

Graphics,

type Tformulaire = class(TForm) edtNum: TEdit; btnButton7: TButton; btnButton1: TButton; btnButton9: TButton; btnButton8: TButton; btnButton6: TButton; btnButton5: TButton; btnButton2: TButton; btnButton4: TButton; btnButton3: TButton; btnButton0: TButton; btnButtonmult: TButton; btnButtondiv: TButton; btnButtonclear: TButton; bnButtonminus: TButton; btnButtonplus: TButton; btnButtonequal: TButton; btnButtonArret: TButton; procedure FormCreate(Sender: TObject); procedure btnButton0Click(Sender: TObject); procedure btnButtonplusClick(Sender: TObject); procedure bnButtonminusClick(Sender: TObject); procedure btnButtonmultClick(Sender: TObject); procedure btnButtondivClick(Sender: TObject); procedure btnButtonclearClick(Sender: TObject); procedure btnButtonequalClick(Sender: TObject); procedure btnButtonArretClick(Sender: TObject); private { Private declarations } public { Public declarations } flag, n1, n2, op : integer; n3 : real;
page 65 de 87

end; var formulaire: Tformulaire; implementation {$R *.dfm} procedure Tformulaire.FormCreate(Sender: TObject); begin flag:=1; end; procedure Tformulaire.btnButton0Click(Sender: TObject); var strNum:string; begin if Sender=btnButton0 then strNum:='0' else if Sender=btnButton1 then strNum:='1' else if Sender=btnButton2 then strNum:='2' else if Sender=btnButton3 then strNum:='3' else if Sender=btnButton4 then strNum:='4' else if Sender=btnButton5 then strNum:='5' else if Sender=btnButton6 then strNum:='6' else if Sender=btnButton7 then strNum:='7' else if Sender=btnButton8 then strNum:='8' else strNum:='9'; if flag=0 then edtNum.Text:=edtNum.Text + strNum else begin edtNum.Text:=strNum; flag:=0 end; end; procedure Tformulaire.btnButtonplusClick(Sender: TObject); begin n1:=strtoint(edtNum.Text); flag := 1; op := 1; end; procedure Tformulaire.bnButtonminusClick(Sender: TObject); begin n1:=strtoint(edtNum.Text); flag := 1; op := 2; end;

page 66 de 87

procedure Tformulaire.btnButtonmultClick(Sender: TObject); begin n1:=StrToInt(edtNum.Text); flag := 1; op := 3; end; procedure Tformulaire.btnButtondivClick(Sender: TObject); begin n1:=StrToInt(edtNum.Text); flag := 1; op := 4; end; procedure Tformulaire.btnButtonclearClick(Sender: TObject); begin edtNum.Text := ''; flag := 1; end; procedure Tformulaire.btnButtonequalClick(Sender: TObject); begin n2:=StrToInt(edtNum.Text); case op of 1: n3:=n1+n2; 2: n3:=n1-n2; 3: n3:=n1*n2; 4: n3:=n1/n2; end;//case edtNum.Text:=FloatToStr(n3); flag := 1; end; procedure Tformulaire.btnButtonArretClick(Sender: TObject); begin Application.Terminate; end; end.

7.8.3

Explication du code

Les mthodes invoques par les boutons 0...9 de la calculatrice tant similaires, on peut se servir dune seule procdure (au lieu de dix !!) pour ragir lactionnement des diffrentes touches numriques. Cette procdure commune tant dfinie uniquement pour lvnement btnButton0Click procedure Tformulaire.btnButton0Click(Sender : TObject) , les neuf autres touches numriques doivent donc produire le mme vnement. Pour cela, on choisira dans longlet Events de linspecteur dobjets la bonne procdure btnButton0Click. Pour discerner la touche numrique qui a dclench la procdure et ainsi donner la bonne valeur la variable strNum, on compare le contenu de la variable Sender avec les noms des diffrentes touches numriques (if Sender = btnButton ... then ...). page 67 de 87

Les mmes variables flag, op, n1, n2 et n3 sont utilises dans toutes les procdures, et sont donc dclares dans len-tte du programme (variables globales). La variable flag est initialise 0 lors du lancement du formulaire (vnement FormCreate du formulaire). Si flag = 0, le chiffre correspondant la touche numrique actionne est concatn la chane de caractres se trouvant dj dans edtNum.Text. Si par contre flag = 1, le chiffre correspondant la touche numrique actionne est copi dans edtNum.Text tout en crasant le contenu antrieur. En cliquant sur un signe dopration (+ , - , * , /), le contenu de la proprit Text de lobjet edtNum est copi dans la variable n1 et constitue le premier oprande. La variable op est initialise avec le code correspondant lopration vise et la valeur 1 est assigne la variable flag. Ainsi le chiffre suivant tap sur les touches numriques de la calculatrice crase le contenu de laffichage (car flag = 1) et constitue le premier chiffre du deuxime oprande. La variable flag est alors remise 0 et les chiffres suivants sont concatns au deuxime oprande. En tapant sur la touche ( = ), le contenu de la proprit Text de lobjet edtNum est copi dans la variable n2 et consitue le deuxime oprande. Lopration dfinie par le code contenu dans la variable op est alors effectue laide de la structure alternative choix multiples (instruction case ... of ...). Il reste remarquer que la calculatrice ne respecte pas la priorit des oprations.

7.9 Calcul matriciel - utilisation du composant StringGrid


Le prochain programme que nous allons tablir est un programme qui manipule les oprations sur les matrices carres 2x2. Exercice : Il est laiss au lecteur la possibilit de changer ce programme pour la manipulation des matrices carres 3 dimensions.

7.9.1

Le composant StringGrid

Dans cet exercice nous utilisons le type prdfini : matrice ou StringGrid qui se trouve dans la barre des objets sous Additional. Il possde de nouvelles proprits dont nous numrons ici les plus importantes :
proprit ColCount RowCount FixedCols FixedRows DefaultColWidth DefaultRowHeight Cells goEditing (Options) Boolean Type Integer Integer Integer Integer Integer Integer explications nombre de colonnes nombre de lignes nombre de colonnes den-ttes nombre de lignes denttes largeur des colonnes (pixels) hauteur des colonnes (pixels) ensemble de cellules indique si lutilisateur peut introduire des valeurs dans les cellules

page 68 de 87

Nous rfrenons une cellule par Cells[colonne,ligne]. Attention : nous devons faire attention que le comptage des lignes et des colonnes commence, comme si souvent, par 0.

7.9.2

Le composant ListBox

Dans cet exemple nous utilisons aussi un nouveau type, la ListBox. Elle sert afficher plusieurs lignes de caractres et donner la possibilit lutilisateur de choisir une ligne prcise. Nous trouvons la ListBox dans la barre des composants standards. Trois proprits sont importantes relever :

Proprit Items ItemIndex Sorted

type string entier boolen

explications tableau des lignes de la liste indice de la ligne slectionne lignes tries ou non

7.9.3

Prsentation visuelle

Commenons dabord, comme dans les exercices prcdents, par tablir un formulaire qui nous sert saisir les donnes et les afficher. Voici un exemple dun tel formulaire.

Ce formulaire prsente les composants suivants :


name lblTitre lblEg1 lblEg2 type TLabel TLabel TLabel text caption Calculs sur les matrices = =

page 69 de 87

lblInv btnEff btnInv btnArret

TLabel TButton TButton TButton

inv effectuez inverse Stop

name lbOp

type TListBox

items +-*

name sgMat1 sgMat2 sgMat3 sgMatInv sgMatRes

type TStringGrid TStringGrid TStringGrid TStringGrid TStringGrid

colcount/rowcount 2/2 2/2 2/2 2/2 2/2

fixedcols/fixedrows 0/0 0/0 0/0 0/0 0/0

Les composants de type StringGrid qui servent introduire des matrices doivent avoir loption goEditing avec la valeur True. Le champ lbOp de type ListBox sert numrer les diffrentes oprations et donner lutilisateur la possibilit de choisir. Aprs llaboration de ce formulaire nous pouvons crire le code ncessaire. Voici un exemple possible.

unit Unit1 ; interface uses Windows, Messages, SysUtils, Variants, Classes, Controls, Forms, Dialogs, StdCtrls, Grids, Buttons ; type Tformulaire = class(TForm) lblTitre : Tlabel ; sgMat1 : TstringGrid ; sgMat2 : TstringGrid ; sgMatRes : TstringGrid ; sgMat3 : TstringGrid ; sgMatInv : TstringGrid ; lblEg1: Tlabel; lblEg2: Tlabel; lblInv: Tlabel; lbOp: TlistBox; btnEff: Tbutton; btnInv: Tbutton; btnArret: Tbutton;
page 70 de 87

Graphics,

procedure btnEffClick(Sender: Tobject); procedure btnInvClick(Sender: Tobject); procedure btnArretClick(Sender: Tobject); private { Private declarations } public { Public declarations } end;//Tformulaire var formulaire: Tformulaire; implementation {$R *.dfm} procedure Tformulaire.btnEffClick(Sender: TObject); var a,i,j : integer; begin if lbOp.ItemIndex=0 then for i:=0 to 1 do for j:=0 to 1 do begin
a:=StrToInt(sgMat1.Cells[i,j])+StrToInt(sgMat2.Cells[i,j]);

sgMatRes.Cells[i,j]:=IntToStr(a); end; //j //i //fi index=0 if lbOp.ItemIndex=1 then for i:=0 to 1 do for j:=0 to 1 do begin
a:= StrToInt(sgMat1.Cells[i,j])-StrToInt(sgMat2.Cells[i,j]);

sgMatRes.Cells[i,j]:=IntToStr(a); end; //j //i //fi index=1 if lbOp.ItemIndex=2 then for i:=0 to 1 do for j:=0 to 1 do begin
a:=StrToInt(sgMat1.Cells[0,j])*StrToInt(sgMat2.Cells[i,0]) +StrToInt(sgMat1.Cells[1,j])*StrToInt(sgMat2.Cells[i,1]);

sgMatRes.Cells[i,j]:=IntToStr(a); end; //j //i //fi index=0 end;//btnEffClick procedure Tformulaire.btnInvClick(Sender: TObject); var a,det:real; begin
page 71 de 87

det:=StrToInt(sgMat3.Cells[0,0])*StrToInt(sgMat3.Cells[1,1]) -StrToInt(sgMat3.Cells[1,0])*StrToInt(sgMat3.Cells[0,1]); a:= 1/det*StrToFloat(sgMat3.Cells[1,1]); sgMatInv.Cells[0,0]:=FloatToStr(a); a:= (-1/det)*StrToInt(sgMat3.Cells[1,0]); sgMatInv.Cells[1,0]:=FloatToStr(a); a:= (-1/det)*StrToInt(sgMat3.Cells[0,1]); sgMatInv.Cells[0,1]:=FloatToStr(a); a:= 1/det*StrToInt(sgMat3.Cells[0,0]); sgMatInv.Cells[1,1]:=FloatToStr(a); end;//btnInvClick procedure Tformulaire.btnArretClick(Sender: TObject); begin Application.Terminate end;//btnArret end.//Unit1

7.9.4

Explication du code

Comme les oprations +, - et * exigent deux oprateurs, mais que lopration inverse na besoin que dun seul, nous avons pu regrouper les trois premiers sous une seule procdure. La TListBox lbOp nous donne les valeurs suivantes :

ItemIndex 0 1 2

opration addition soustraction multiplication

Pour le reste, le contenu des diffrentes parties des procdures correspond aux rgles mathmatiques qui dfinissent les oprations sur les matrices.

page 72 de 87

8 La rcursivit
8.1 Exemple
La fonction suivante calcule une puissance de base relle non nulle et dexposant naturel :
function puissance(x:real;m:integer):real; begin if m=0 then result:=1 else result:=x*puissance(x,m-1) end;

Cette fonction prsente une grande diffrence par rapport toutes les fonctions que nous avons dfinies prcdemment. Dans la dfinition mme on trouve dj un appel la fonction puissance. Il sagit ici dun mcanisme trs puissant, prsent dans tous les langages de programmation modernes : la rcursivit. Le fonctionnement exact de ce mcanisme ainsi que les conditions dutilisation seront tudies en dtail dans les paragraphes suivants. Remarquons cependant quil existe un lien troit entre la rcursivit en informatique et la rcurrence en mathmatique. La dfinition de la fonction puissance prsente ici est une transcription quasi directe des formules x0 = 1 , valables pour x non nul5. m m 1 x = x x

8.2 Dfinition : fonction ou procdure rcursive


On dit quune fonction ou une procdure est rcursive (de manire directe) si elle sappelle elle-mme. Une fonction ou une procdure est rcursive de manire indirecte si elle appelle une autre fonction ou procdure qui rappelle la premire de faon directe ou indirecte.
La fonction puissance du paragraphe prcdent est bien sr une fonction rcursive directe. Le mcanisme de la rcursivit est trs puissant, mais il faut une certaine exprience pour pouvoir lutiliser dans de bonnes conditions. Dans la suite nous allons lucider principalement les aspects suivants : Sous quelles conditions et pourquoi une fonction rcursive donne-t-elle le rsultat attendu ? Comment vrifier quune telle fonction est correcte ? Comment le systme gre-t-il une fonction rcursive ? Cest--dire comment est-ce que ce mcanisme fonctionne en pratique ? Est-ce quune fonction rcursive est meilleure ou moins bonne quune fonction itrative (normale) ?

Il est clair que ces diffrents aspects ne sont pas indpendants les uns des autres, mais quil faut une vue densemble pour bien les comprendre.

La fonction puissance donne un rsultat incorrect si x et m sont nuls. De plus, il est ncessaire que m soit un entier positif !

page 73 de 87

8.3 Etude dtaille dun exemple


Dans ce paragraphe nous revenons la fonction puissance de la page prcdente et nous allons commencer par tudier quelques exemples dexcution.

puissance(7,0) : donne bien sr comme rsultat 1 vu que la condition m=0 est vrifie. puissance(7,1) : la condition m=0 nest pas vrifie et la fonction calcule donc dabord x*puissance(x,m-1) cest--dire 7*puissance(7,0) ce qui donne dans une deuxime tape 7*1=7. puissance(7,2) : ici la condition m=0 nest pas non plus vrifie et la fonction calcule donc aussi dabord x*puissance(x,m-1) cest--dire 7*puissance(7,1) . Suivent ensuite les deux tapes prcdentes. A la fin de la troisime tape le rsultat obtenu est 7*puissance(7,1)=7*[7*puissance(7,0)]=7*7*1=49.
Il est important de remarquer ici que le systme refait chaque fois toutes les tapes et ne se souvient pas des appels de fonctions prcdents. Lexcution de puissance(7,12) ncessite 13 passages dans la fonction : dabord 12 appels rcursifs et ensuite un dernier passage o m=0. Lexemple puissance(7,-2) est particulirement intressant. La condition m=0 nest pas vrifie et la fonction calcule donc x*puissance(x,m-1) cest--dire 7*puissance(7,-3). Ensuite elle va valuer 7*puissance(7,-4), 7*puissance(7,-5), 7*puissance(7,-6), etc. Il est clair que cet appel ne va certainement pas donner le rsultat 1/49. Mais la situation est plus grave, lexcution de la fonction ne va pas donner de faux rsultat, mais cette excution ne va pas se terminer6 vu que la condition m=0 ne sera plus jamais vrifie. Pour quune fonction ou une procdure rcursive sarrte, il est ncessaire que le code vrifie les conditions suivantes :

Pour une ou plusieurs valeurs des donnes, appeles cas de base , la fonction calcule directement (sans appel rcursif) le rsultat. Dans le code de la fonction il doit tre assur que chaque suite dappels rcursifs va toujours finir par atteindre un cas de base.

Il est clair que le fait que la fonction sarrte, signifie seulement quelle va fournir un rsultat, mais non pas que ce rsultat est correct. Larrt de la fonction est une condition pralable ! Dans notre exemple, il est donc ncessaire de prciser que la fonction puissance ne convient pas pour les exposants ngatifs7. Dans une dmonstration par rcurrence en mathmatiques la situation est semblable : Pour montrer quune proprit est vraie pour tout entier naturel, on montre dabord quelle est vraie pour le cas de base n=0 et ensuite on montre que si la formule est vraie pour le naturel n, alors elle reste vraie pour n+1. La vracit de la proprit pour n=4 est alors ramene successivement n=3, n=2, n=1 jusquau cas de base n=0, que lon sait vrifi.

Dans cette situation le systme risque dentrer dans un tat indfini provoqu par un dbordement de mmoire. Dans le pire cas il sera ncessaire de redmarrer lordinateur (RESET) avec toutes les consquences que cela peut impliquer. Dans un cas plus favorable, Delphi dtecte le problme et avorte lexcution de la fonction.

Elle ne convient pas non plus pour les exposants non entiers, mais ceux-ci sont de toute faon exclus vu que la variable exposant m est de type integer.

page 74 de 87

8.4 Fonctionnement interne


Dans ce paragraphe on va chercher une rponse la question comment le systme gre lexcution dune procdure rcursive. La bonne comprhension de ce mcanisme est importante pour pouvoir rdiger des programmes efficaces. Revenons lexemple de la fonction factorielle qui calcule la factorielle dun nombre naturel donn. Cet exemple, dj implment de faon itrative au chapitre prcdent, se prte particulirement bien tre programm de faon rcursive vu que la dfinition mathmatique de la fonction se base sur des formules de rcurrence.

0!= 1 0!= 1 ou bien n!= n (n 1)! si n N 0 n!= n (n 1) (n 2) K 3 2 1 si n N 0

function factorielle(n:integer):integer; begin if n=0 then result:=1 else result:=n*factorielle(n-1) end;

On peut lintgrer dans le programme Delphi du chapitre prcdent. Le reste du code reste identique. Lors de lexcution de factorielle(1) le systme excute la fonction jusqu lappel rcursif factorielle(n-1) A ce stade le systme mmorise ltat actuel de toutes les variables locales de la fonction. Lors de lexcution de factorielle(n-1) le systme recommence excuter la fonction factorielle avec le paramtre n-1=0. Lors de ce deuxime passage dans la fonction, les variables locales sont rinitialises, leurs anciennes valeurs ne sont pas accessibles ce moment mais elles sont sauvegardes pour une rutilisation ultrieure. Maintenant on a donc n=0 et le systme termine ce passage dans la fonction avec result:=1. Ensuite le systme revient dans le premier passage de la fonction lendroit factorielle(n-1). Les variables locales rcuprent leur ancienne valeur et lexpression n*factorielle(n-1) est value avec n=1 et factorielle(n-1)=1. La variable result prend la valeur 1 et lexcution quitte dfinitivement la fonction. Pour des appels de fonction avec des arguments plus grands, par exemple factorielle(15) le systme doit donc conserver autant de copies de toutes les variables locales quil y a dappels rcursifs avant datteindre le cas de base. Cela peut ncessiter une quantit apprciable de mmoire et de temps dexcution pour la gestion qui en dcoule.

8.5 Exactitude dun algorithme rcursif


Il est souvent facile de montrer quune fonction rcursive donne le bon rsultat. Normalement le raisonnement par rcurrence simpose. Pour la fonction factorielle, par exemple : Base : n=0, le rsultat est effectivement 1. Hypothse de rcurrence : supposons que la fonction factorielle donne le bon rsultat jusquau rang n-1 pour n>0, cest--dire que factorielle(n-1)=(n-1)!. Pont : montrons que la fonction donne encore le bon rsultat au rang n, donc que factorielle(n)=n!. En effet, la fonction donne le rsultat n*factorielle(n-1) qui est

page 75 de 87

gal n*(n-1)! par lhypothse de rcurrence et qui est encore gal n! par la dfinition mathmatique de la factorielle. Cette dmonstration ne prend pas en compte des problmes de dpassement de mmoire, inhrents au systme Delphi, si n est trop grand .

8.6 Comparaison : fonction rcursive fonction itrative8


Lexemple suivant est particulirement impressionnant.

u 0 = 1 et u1 = 1 La clbre suite de Fibonacci qui est dfinie par , simplmente u n = u n1 + u n 2 , n N { 0 ;1 } directement par la fonction rcursive suivante :
function fibo(n:integer): int64; begin if n<=1 then result:=1 else result:=fibo(n-1)+fibo(n-2) end;

Le type du rsultat est int64, une variante de integer qui permet de reprsenter des nombres entiers plus grands. La condition n<=1 prend en charge les deux cas de base n=0 et n=1. La fonction suivante, dont lalgorithme est bas sur une simple boucle nest pas vraiment difficile comprendre non plus. Si n est diffrent de 0 et de 1, alors tous les termes de la suite jusquau nime sont calculs de proche en proche (comme on le ferait si on navait pas dordinateur disposition).

function fibo_it(n:integer): int64; var t0,t1: int64; begin if n<=1 then result:=1 else begin t0:=1; t1:=1; while n>1 do begin result:=t0+t1; t0:=t1; t1:=result; n:=n-1 end end end;
Pour se faire une ide de la situation, il est conseill de tester les 2 fonctions. En prenant successivement les arguments 5, 10, 20, 30, 40, 50, on va finir par constater la mme chose indpendamment de lordinateur utilis. Mme sur un ordinateur rapide la fonction rcursive va finir par devenir terriblement lente alors que la fonction itrative va rester rapide (rsultat immdiat) mme sur un ordinateur trs faible ! Pourquoi ?

Iteratif: algortihme bas sur une boucle.

page 76 de 87

Que fait la fonction itrative ? Si n>1, cette fonction effectue exactement n-1 additions sur des nombres du type int64, et un nombre approximativement proportionnel n daffectations, de soustractions et de comparaisons. Le temps dexcution de ces oprations est ngligeable par rapport la croissance fulgurante des rsultats : cest--dire la fonction sera limite par la capacit de reprsentation du type int64 avant quon remarque un quelconque ralentissement dans lexcution ! Que fait la fonction rcursive ? Etudions les excutions de la fonction pour les arguments 2 5. Pour fibo(2) [rsultat : 2], la condition nest pas vrifie et la fonction calcule donc lexpression fibo(1)+fibo(0). Les deux appels rcursifs donnent directement le rsultat 1 et lexcution se termine donc assez rapidement aprs seulement 3 passages (chaque fois 1 appel avec les arguments 0, 1 et 2) dans la fonction. Pour fibo(3) [rsultat : 3], la fonction calcule dabord fibo(2)+fibo(1). Le premier appel ncessite trois passages (voir alina prcdent) dans la fonction et le deuxime appel donne directement 1. Cela fait donc un total de 5 appels. Pour fibo(4) [rsultat : 5], la fonction calcule fibo(3)+fibo(2). Le nombre dappels se calcule par 5 (pour fibo(3)) + 3 (pour fibo(2)) + 1 (pour fibo(4)) = 9. Sans entrer dans tous les dtails : Pour fibo(10) [rsultat : 89], il faut 177 appels, pour fibo(20) [rsultat : 10946], il faut 21891 appels et pour fibo(30) [rsultat : 1346269], il faut 2692537 appels. Il est clair que le nombre dappels de fonctions doit tre suprieur ou gal au rsultat. En effet, les deux seuls cas de bases donnent le rsultat 1 et lappel rcursif consiste ajouter deux rsultats intermdiaires. Le rsultat final est donc atteint en ajoutant tant de fois le nombre 1. A ct du nombre impressionnant de calculs que la fonction effectue, il ne faut pas oublier la quantit despace mmoire ncessaire pour sauvegarder toutes les valeurs intermdiaires de la variable n.

8.7 Rcursif ou itratif ?


Contrairement ce que lexemple prcdent pourrait suggrer, un algorithme rcursif nest pas automatiquement moins bon quun algorithme itratif. Si on tient compte de lvaluation interne dune fonction rcursive, on peut parfois trouver une variante efficace de la fonction rcursive. Voici par exemple une formulation rcursive efficace de la fonction fibo. Lastuce consiste utiliser une fonction auxiliaire rcursive avec deux arguments supplmentaires, ncessaires pour passer les deux termes prcdents lappel suivant. On remarquera que dans le code de cette fonction il ny a quun seul appel rcursif, de faon ce que le nombre dappels pour calculer le nime terme de la suite de Fibonacci ne dpasse pas n.

function fibo(n:integer):int64; function fib_aux(n:integer;i,j: int64): int64; begin if n<=1 then result:=i else result:=fib_aux(n-1,i+j,i) end; begin result:=fib_aux(n,1,1) end;

page 77 de 87

Un certain nombre de problmes admettent une solution rcursive trs lgante (algorithme simple rdiger et comprendre, mais pas ncessairement efficace). Nous verrons des exemples de ce genre dans le chapitre sur les recherches et les tris9. Pour certains de ces problmes, une formulation laide de boucles est trs complique. Mais une telle formulation existe toujours. Dans le pire des cas, il est ncessaire de simuler lalgorithme rcursif en stockant toutes les valeurs intermdiaires des variables dans un tableau. Cest de cette faon-l que le programme Delphi lui-mme value les fonctions rcursives ; en effet le langage machine, seul langage compris par le processeur, est un langage relativement faible qui ne connat pas le mcanisme de la rcursivit.

8.8 Exercices
Exercice 8-1
a) Ecrivez un programme qui calcule la fonction dAckermann deux variables naturelles dfinie par les galits :
Ack (0 , m ) = m + 1 Ack (k , 0 ) = Ack (k 1,1) si k 1 Ack (k , m ) = Ack (k 1, Ack (k , m 1)) si k , m 1

b) Essayez cette fonction pour quelques ack(4,0)=13, ack(4,1)=65533,

arguments.

Par

exemple

ack(3,5)=253,

Exercice 8-2
Compltez la fonction puissance pour quelle calcule aussi correctement des puissances exposant entier ngatif.

Exercice 8-3
Ecrivez une version rcursive de la fonction puissance rapide.
a 0 = 1 Aide : utilisez les formules a 2 n = a 2 n 2 n +1 = a 2n a a

( )

Expliquez pour quelles valeurs de a et de n, lexcution va sarrter et donner un rsultat correct.

Exercice 8-4
Ecrivez une version rcursive de la fonction pgcd.

Exercice 8-5
Ecrivez un programme rcursif qui transforme un nombre binaire en notation dcimale.

Exercice 8-6
Ecrivez un programme rcursif qui transforme un nombre dcimal en notation binaire.

Voir par exemple recherche dichotomique et quicksort .

page 78 de 87

9 Comptage et frquence
9.1 Algorithme de comptage
On veut raliser une fonction qui compte le nombre doccurrences dune chane de caractres dans une liste donne. Pour raliser cet algorithme on parcourt la liste lment aprs lment et on le compare avec la chane trouve. Si les deux chanes sont identiques, on augmente un compteur.

function compter(liste:TListbox;chaine:string):integer; var i,r:integer; begin r:=0; for i:=0 to liste.Items.Count-1 do if liste.Items[I] = chaine then r:=r+1; result:=r; end; Exercice 9-1
Ralisez une fonction qui permet de compter le nombre doccurrences dune lettre dans une chane de caractres donne.

9.2 Frquence dune lettre dans une liste


Soit une liste dont les lments sont des lettres. On veut savoir combien de fois chaque lettre est reprsente dans la liste. Ce type dalgorithme est un lment trs important des algorithmes de compression, comme par exemple celui de Huffman. La solution propose utilise de manire un peu inhabituelle les tableaux, mais de ce fait-l devient trs lgante. On utilise comme indice du tableau des lettres. Le rsultat de lanalyse de la liste se trouve dans une deuxime liste passe comme paramtre et a le format suivant : lettre : nb. doccurrences.

procedure frequence(liste:TListbox; var resultat:TListbox); var i:integer; c:char; tableau:array['A'..'Z'] of integer; element:string; begin for c:='A' to 'Z' do tableau[c]:=0; for i:=0 to liste.Items.Count-1 do begin element:=liste.Items[i]; tableau[element[1]]:=tableau[element[1]]+1; end; resultat.Items.Clear; for c:='A' to 'Z' do resultat.Items.Append(c+' : '+inttostr(tableau[c])); end;

page 79 de 87

Exercice 9-2
Ralisez un sous-programme qui permet de compter le nombre doccurrences des diffrentes lettres dans une chane de caractres donne. Le rsultat du sous-programme est un tableau.

10 Recherche et tri
10.1 Introduction
Les algorithmes de recherche et de tri ont une trs grande importance en informatique. Cest surtout dans le contexte des bases de donnes que ces algorithmes sont utiliss. Nous allons traiter en premier lieu les algorithmes de tri car pour fonctionner certains algorithmes de recherche prsument des donnes tries. Bien que lillustration des diffrents algorithmes se base sur un petit nombre de donnes, il ne faut pas oublier que ces algorithmes sont en gnral appliqus sur un nombre trs grand de donnes ( plus que 10000, comme par exemple dans un annuaire tlphonique ).

10.2 Sous-programmes utiles


Nous allons dfinir deux sous-programmes qui vont nous permettre de faciliter la programmation et le test des algorithmes de tri et de recherche.

10.2.1

Echange du contenu de deux variables entires

procedure echange (var liste:TListbox; posa,posb:integer); var temp: string; begin temp:=liste.Items[posa]; liste.Items[posa]:=liste.Items[posb]; liste.Items[posb]:=temp; end;
Cette procdure change le contenu de deux lments dune liste.

10.2.2

Remplissage dune liste

procedure remplissage(var liste: TListbox; n: integer); const a=ord('A'); var i,j:integer; s : string ; begin liste.Clear; for i:= 1 to n do begin s:=''; for j:=0 to random(5)+1 do s:=s+chr(random(26)+a); liste.Items.Append(s); end; end;
Cette procdure remplit une liste avec n mots (comportant de 2 6 lettres) pris au hasard. page 80 de 87

10.3 Les algorithmes de tri


Le but dun algorithme de tri est d'arranger des donnes selon un critre impos. Ce critre reprsente une relation entre les donnes comme par exemple le tri dune liste de mots selon lordre lexicographique ou le tri dune liste de nombre en ordre croissant.

10.3.1
10.3.1.1

Tri par slection


Ide

On cherche dans la liste le plus petit lment et on lchange avec le premier lment de la liste. Ensuite on recommence lalgorithme en ne prenant plus en considration les lments dj tris et ceci jusqu ce quon arrive la fin de la liste.

10.3.1.2 Solution rcursive procedure tri_selection_r(var liste:TListbox; debut:integer); var j,min:integer; begin min:=debut; for j:=debut+1 to liste.Items.Count-1 do if liste.Items[j]<liste.Items[min] then min:=j; echange(liste,debut,min); if debut < liste.Items.Count-2 then tri_selection_r(liste,debut+1); end; 10.3.1.3 Solution itrative procedure tri_selection_i(var liste:TListbox); var i,j,min:integer; begin for i:=0 to liste.Items.Count-2 do begin min:=i; for j:=i+1 to liste.Items.Count-1 do if liste.Items[j]<liste.Items[min] then min:=j; echange(liste,i,min); end; end; 10.3.1.4 Exercice

Excutez pas pas les deux algorithmes en utilisant la liste suivante : E ;X ;E ;M ;P ;L ;E.

page 81 de 87

10.3.2
10.3.2.1

Tri par insertion


Ide

On considre un lment aprs lautre dans la liste et on cherche sa position dans la liste dj trie. Ensuite on linsre la juste position. De ce fait les lments suivants de la liste doivent tre dplacs.

10.3.2.2 Solution rcursive procedure tri_insertion_r(var liste: TListbox;gauche,droite: integer); var j:integer; candidat:string; begin if gauche<droite then begin tri_insertion_r(liste,gauche,droite-1); candidat:=liste.Items[droite]; j:=droite; while (j>0) and (liste.Items[j-1] > candidat) do begin liste.Items[j]:=liste.Items[j-1]; j:=j-1; end; if j<droite then liste.Items[j]:=candidat; end; end;

10.3.2.3 Solution itrative procedure tri_insertion_i(var liste:TListbox); var i,j:integer; candidat:string; begin for i:= 1 to liste.Items.Count-1 do begin candidat:=liste.Items[i]; j:=i; while (j>0) and (liste.Items[j-1] > candidat) do begin liste.Items[j]:=liste.Items[j-1]; j:=j-1; end; if j<droite then liste.Items[j]:=candidat; end; end; 10.3.2.4 Exercice

Excutez pas pas les deux algorithmes en utilisant la liste suivante : C;A;R;T;O;O;N. page 82 de 87

10.3.3
10.3.3.1

Tri rapide
Introduction

Le tri rapide (ang. : Quicksort) est lalgorithme de tri le plus utilis. Il est rput pour sa vitesse de tri qui pour des listes de grande taille est souvent bien suprieure celle dautres algorithmes de tri. Nous allons dvelopper le tri rapide en plusieurs tapes.

10.3.3.2

Ide

Lalgorithme de tri va diviser la liste en deux parties. Ces parties qui ne sont pas forcment de taille gale, sont alors tries sparment par le mme algorithme. Limportant dans cet algorithme est la stratgie comment la liste est divise en deux sous-listes.

10.3.3.3

Dveloppement dune solution rcursive

Lide de base nous conduit un algorithme rcursif trs simple :

procedure tri_rapide_r(var liste:TListbox;g,d:integer); var i:integer; begin if d>g then begin i:=division(liste,g,d); tri_rapide_r(liste,g,i-1); tri_rapide_r(liste,i+1,d); end; end;
Les paramtres g et d limitent la partie de la liste qui doit tre trie. Le premier appel de la procdure de tri a comme paramtres lindice du premier et lindice du dernier lment de la liste.

10.3.3.4

Division de la liste

Pour que cette procdure rcursive fonctionne correctement il faut que la fonction division remplisse trois conditions : 1. Llment avec lindice i (indice qui est retourn comme rsultat) se trouve lendroit dfinitif. 2. Tous les lments gauche de llment avec lindice i sont plus petits ou gaux celui-ci. 3. Tous les lments droite de llment avec lindice i sont plus grands ou gaux celui-ci. Ces trois conditions nous amnent une situation trs agrable : La premire nous dit que llment avec lindice i na plus besoin dtre dplac. La deuxime implique quaucun lment de la sous-liste gauche ne sera dplac au-del de llment avec lindice i. La troisime implique quaucun lment de la sous-liste droite ne sera dplac avant llment avec lindice i. En plus on peut affirmer que les deux sous-listes obtenues de cette manire-ci peuvent tre tries par la suite dune manire indpendante.

page 83 de 87

La fonction division se prsente de la manire suivante :

function division(var liste: TListbox;g,d:integer):integer; var i,j : integer; candidat :string; begin candidat:=liste.Items[d]; j:=d-1; i:=g; while i<=j do begin if liste.Items[i]< candidat then i:=i+1 else if liste.Items[j]> candidat then j:=j-1 else begin echange(liste,i,j); i:=i+1 ; j:=j-1; end; end; echange(liste,i,d); result:=i; end;
Pour commencer, on choisit llment qui se trouve la fin de liste comme candidat. On va dterminer la position dfinitive du candidat dans la liste et sarranger que tous les lments de la liste qui se trouvent avant sont plus petits et tous ceux qui se trouvent aprs sont plus grands que le candidat. La boucle parcourt la liste en commenant par le dbut (if) jusquau premier lment suprieur au candidat, ensuite (else if) elle parcourt la liste en commenant par la fin jusquau premier lment infrieur au candidat. Ces 2 lments sont alors (else) changs et on recommence. A la fin de la boucle le candidat est mis sa place dfinitive et lindice de cette place est rendu comme rsultat de la fonction. On vrifie facilement quau cas o le candidat se trouvait sa bonne place (lorsquil est le plus grand lment de la liste), alors i=d et le dernier change na aucun effet.

10.3.4

Tri par fusion

Cet algorithme ne figure pas au programme.

10.3.4.1 Ide Si lon dispose de deux listes tries, on peut assez facilement les fusionner10 (mettre ensemble) afin dobtenir une seule liste trie. Un tri par fusion utilisant cette approche peut tre implment de faon rcursive et de faon itrative. Les deux versions utilisent la mme procdure fusion. 10.3.4.2 Fusion de deux liste tries

Lalgorithme de fusion utilise un tableau auxiliaire de chanes de caractres, de mme taille que la liste trier. Ce tableau peut tre dclar globalement ; cela vite de redclarer le tableau chaque appel de la procdure. Les deux sous-listes fusionner se trouvent de faon conscutive dans la liste. Les paramtres g et m sont les indices de dbut et de fin de la premire sous-liste, alors que la seconde sous-liste commence lindice m+1 et se termine en d. Les deux sous-listes sont dabord copies dans le tableau auxiliaire : la 1re de faon croissante et la 2e de faon dcroissante de sorte que les lments les plus grands se trouvent au milieu. Ensuite la procdure commence comparer les plus petits

La fusion est en quelque sorte une gnralisation de linsertion o lon fusionne aussi deux listes tries, dont lune nest compose que dun seul lment.

10

page 84 de 87

lments des deux-sous-listes (le plus gauche et le plus droite) et replace le plus petit des deux dans la liste principale et ainsi de suite.

procedure fusion(var liste: TListbox; g,m,d: integer); var i,j,k : integer; tt : array[1..1000] of string; {mieux: dclarer tt globalement} begin for i:=g to m do tt[i]:=liste.Items[i]; for j:=m+1 to d do tt[d+m+1-j]:=liste.Items[j]; i:=g; j:=d; for k:=g to d do if tt[i]<tt[j] then begin liste.Items[k]:=tt[i]; i:=i+1 end else begin liste.Items[k]:=tt[j]; j:=j-1 end; end; 10.3.4.3 Solution rcursive Il suffit de diviser la liste en deux, de trier sparment les deux parties et de fusionner les deux parties tries. procedure tri_fusion_re(var liste: TListbox;g,d:integer); var m:integer; begin if g<d then begin m:=(g+d) div 2; tri_fusion_re(liste,g,m); tri_fusion_re(liste,m+1,d); fusion(liste,g,m,d); end; end; 10.3.4.4 Solution itrative La version itrative commence par considrer des sous-listes de longueur 1 qui sont ensuite fusionnes 2 2. Ensuite la liste nest compose que de sous-listes de longueur 2 dj tries. Cellesci sont encore fusionnes 2 2 et ainsi de suite. Dtape en tape la longueur des sous-listes tries double (variable step). Toute la liste est trie lorsquil nexiste plus quune seule grande sousliste. procedure tri_fusion_it(var liste: TListbox); var i,m,step:integer; begin m:=liste.Items.Count; step:=1; i:=0; while step<m do begin while (i+2*step-1)<m do begin fusion(liste,i,i+step-1,i+2*step-1) ; i:=i+2*step; end; if (i+step)<=m then fusion(liste,i,i+step-1,m-1); {s'il reste une liste et une partie d'une 2e liste}
page 85 de 87

step:=step*2; i:=0 ; end; end;

10.4 Les algorithmes de recherche


Les algorithmes de recherche ont pour but de dterminer lindice dun lment dune liste qui rpond un certain critre. Si lon ne trouve pas llment, on retourne par convention 1. Llment recherch encore appel cl, peut figurer plusieurs fois dans la liste. On suppose que la liste nest pas vide.

10.4.1
10.4.1.1

Recherche squentielle
Ide

Il sagit de lalgorithme de recherche le plus simple qui puisse exister : on commence examiner la liste ds le dbut jusqu ce quon ait trouv la cl. Lalgorithme donne lindice de la premire occurrence de la cl. La liste ne doit pas tre trie.

10.4.1.2 Solution itrative function recherche_seq_i(liste:TListbox;cle:string):integer; var index:integer; begin index:=0; while (index <= liste.Items.Count-1) and (liste.Items[index]<>cle) do index:=index+1; if index <= liste.Items.Count-1 then result:=index else result:=-1; end;

10.4.2
10.4.2.1

Recherche dichotomique
Ide

La recherche dichotomique est trs rapide et utilise une mthode bien connue : Si par exemple, vous devez chercher une localit dans lannuaire tlphonique, vous ne commencez pas la premire page pour ensuite consulter une page aprs lautre, mais vous ouvrez lannuaire au milieu pour ensuite regarder si la localit se situe dans la premire partie ou dans la deuxime partie de lannuaire. Ensuite vous recommencez lopration, c d vous divisez de nouveau la partie contenant la localit recherche en deux et ainsi de suite jusqu ce que vous ayez trouv la localit. Lalgorithme de la recherche dichotomique utilise le mme procd : On divise la liste en deux parties. On regarde si la cl correspond llment au milieu de la liste. Si cest le cas on a termin, sinon on dtermine la partie qui contient la cl et on recommence la recherche dans la partie contenant la cl. La liste ne doit pas forcment contenir la cl, mais elle doit tre trie.

page 86 de 87

10.4.2.2 Solution rcursive function dicho_r(liste:TListbox;cle:string;g,d:integer):integer; var milieu:integer; begin if g>d then result:=-1 else begin milieu:=(g+d) div 2; if liste.Items[milieu] = cle then result:=milieu else if cle<liste.Items[milieu] then result:= dicho_r(liste,cle,g,milieu-1) else result:= dicho_r(liste,cle,milieu+1,d); end; end; 10.4.2.3 Solution itrative

function dicho_i(liste:TListbox;cle:string):integer; var milieu,g,d:integer; begin g:=0; d:=liste.Items.Count-1; milieu:=(g+d) div 2; while (cle<>liste.Items[milieu]) and (g<=d) do begin milieu:=(g+d) div 2; if cle<liste.Items[milieu] then d:=milieu-1 else g:=milieu+1; end; if cle=liste.Items[milieu] then result:=milieu else result:=-1; end;

page 87 de 87