Vous êtes sur la page 1sur 294

118

Licence Sciences
et Technologies
Module API
Approche
imprative.
Mise en uvre en
Java

Juin 2007
Version 4

J. Barr,P. Le
certen,Y. Le
Tertre, L. Morin,
L. Ungaro

Copyright J. Barr,P. Le certen,Y. Le Tertre, L. Morin, L. Ungaro - Ifsic - Universit de


Rennes 1
La reproduction non autorise d'une uvre protge constitue un dlit.

LICENCE SCIENCES ET TECHNIQUES

MTHODES ET OUTILS ALGORITHMIQUES


APPROCHE IMPRATIVE (MODULE API)

Version 4 - juin 2007

Jacques Barr, Pascale Le Certen, Lucien Ungaro

CHAPITRE 1

Introduction 5
1.1 Algorithmes, langages de programmation, programmes 5
1.1.1 Langages de programmation 6
1.1.2 Algorithmes en langages dclaratifs 6
1.1.3 Algorithmes en langages impratifs 8

1.2 Rigueur, syntaxe et smantique 9


1.3 Rsolution dun problme 10

1.3.1 Phase danalyse et phase de programmation


1.3.2 Mthodes danalyse 10
1.3.3 Qualits dun programme 11

10

1.4 Exemple introductif 11

1.4.1 Analyse 12
1.4.2 Rdaction du programme 12
1.4.3 Compilation - excution 14

CHAPITRE 2

Introduction aux types de donnes 19


2.1 Notion de type 19
2.2 Intrt des types 20

2.2.1 Cohrence des donnes 20


2.2.2 Ncessit technologique de reprsenter correctement linformation
2.2.3 Intrt technologique de dterminer les besoins en mmoire 21

20

2.3 Panorama des diverses catgories de types 21


2.4 Utilisations des types 22

2.4.1 Dclarations 22
2.4.2 Exemples doprations sur des donnes de types primitifs

2.5 Description des types scalaires 24


2.5.1 Types primitifs scalaires
2.5.2 Types numrs 27

CHAPITRE 3

23

24

Dclarations, fonctions, expressions, instructions 35


3.1 Structure de programmes simples 35
3.2 Dclarations de donnes 36
3.2.1 Les diverses sortes de donnes 36
3.2.2 Intrt des dclarations de constantes

37

3.3 Dfinitions de fonctions 39


3.4 Commentaires de spcification dune fonction 41
3.5 Instructions et expressions 42
3.5.1 Squences dinstructions 42
3.5.2 Instructions daffichage lcran 43
3.5.3 Expression de concatnation de chanes de caractres
3.5.4 Instruction de retour de fonction 44
3.5.5 Expressions 45
3.5.6 Appel de procdure - Appel de fonction 47
3.5.7 Variables et instruction daffectation 49
3.5.8 Instructions de lecture du clavier 50

Module A.P.I. Algorithmique Programmation Imprative

44

Universit de Rennes 1

CHAPITRE 4

Nommage, portes didentification, dures de vie,


modularit 57
4.1
4.2
4.3
4.4
4.5

CHAPITRE 5

Nommage 57
Portes didentification 58
Dures de vie 59
Identification temporaire - blocs dinstructions 60
Modularit 62
4.5.1 Dcoupage en classes
4.5.2 Paquetages 63

62

Instructions conditionnelles 69
5.1 Forme gnrale de linstruction conditionnelle 69
5.2 Imbrication de conditionnelles 72
5.3 Aiguillage 74

CHAPITRE 6

Rcursivit et itration

81

6.1 Exprimer la rptition 81


6.1.1 Mthode rcursive 81
6.1.2 Mthode itrative 82

6.2 Rcursivit 83

6.2.1 Exemples de dfinitions rcursives de fonctions

6.3 Terminaison dun algorithme rcursif 84


6.4 Gnralits sur lItration 85

83

6.4.1 Forme gnrale dune itration 85


6.4.2 Premier exemple de calcul itratif : racine carre 86
6.4.3 Itration sur une composition de plusieurs variables 86
6.4.4 Trace dexcution dune itration 88
6.4.5 Petits ennuis dus labsence daffectation multiple 89

6.5 Notions dinvariant de boucle et de fonction de terminaison 90


6.5.1 Construction dune itration 90
6.5.2 Invariant de boucle 92
6.5.3 Fonction de terminaison et notion de complexit 94
6.5.4 Autre exemple dinvariant de boucle et de fonction de terminaison

6.6 Itrations sur des entres de donnes 95


6.7 Boucle pour faire n fois 97
6.8 Boucles rsultat polymorphe 100

CHAPITRE 7

95

Type numr, type structure, notion de rfrence 115


7.1 Type numr 115
7.2 Type structure 117

7.2.1 Dfinition dun type structure 117


7.2.2 Cration de structure - dsignation par rfrence 119
7.2.3 Slection de champ 121
7.2.4 Comparaison des rfrences - comparaison des valeurs
7.2.5 Rfrences en paramtre et en rsultat 123
7.2.6 Exemple rcapitulatif 123

Module A.P.I

Algorithmique Programmation Imprative

122

Universit de Rennes 1

CHAPITRE 8

Chanes de caractres 133


8.1 Type chane de caractres 133
8.2 Quelques oprations disponibles sur les chanes de caractres 135
8.3 Exemple de manipulations de chanes de caractres 137

CHAPITRE 9

Tableaux 143
9.1
9.2
9.3
9.4
9.5
9.6
9.7
9.8

CHAPITRE 10

Intrt des tableaux 143


Cration et nommage des tableaux en Java 144
Accs aux lments de tableau 145
Consquences de la dsignation par rfrence 146
Dclarations de tableaux initialiss 147
Exemples dutilisation de tableaux 149
Raisonnement sur les tableaux - notion de tranche 150
Tableaux plusieurs dimensions 154

Objets de type classe - abstraction 173


10.1 Approche variable de type structure - approche objet 173
10.1.1Approche variable structure 173
10.1.2Approche objet 175
10.1.3Citation explicite de linstance courante : this
10.1.4Encapsulation - notion dabstraction 177

176

10.2 Exemple rcapitulatif 180


10.3 Retour sur les composants statiques et non statiques 181
10.4 Commentaires de spcification pour une classe modle
dobjets 183

CHAPITRE 11

Structures de donnes : listes


11.1
11.2
11.3
11.4

191

Notion de structure de donnes 191


Spcification du type liste 192
Exemples dutilisation dune liste 195
Mise en uvre des listes 197
11.4.1Reprsentation des lments dune liste
11.4.2Parcours de listes 201

197

11.5 Prcisions sur les classes internes 206

CHAPITRE 12

Utilisation des fichiers 211


12.1 Fichiers squentiels de texte 211
12.2 Utilisation de fichiers textes en Java
12.2.1Lecture de fichiers textes 212
12.2.2criture de fichiers textes 214

CHAPITRE 13

212

Structures de donnes : ensembles

217

13.1 Spcification du type ensemble 217


13.2 Exemples dutilisation densembles 219
13.3 Mise en uvre des ensembles 219

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

CHAPITRE 14

Structures de donnes : arbres 237


14.1
14.2
14.3
14.4
14.5
14.6
14.7

CHAPITRE 15

Spcification de la classe ArbreBinaire


238
Exemples simples dutilisation de la classe ArbreBinaire
Mise en uvre de la classe ArbreBinaire
240
Autre exemple dutilisation de ArbreBinaire
244
Spcification de la classe Arbre
245
Exemple dutilisation de la classe Arbre
246
Mise en uvre de la classe Arbre
249

Reprsentation des informations dans les


ordinateurs 253
15.1 Nombres et reprsentations de nombres 253
15.1.1Notion de reprsentation 253
15.1.2Numrations positionnelles 253
15.1.3Chiffres binaires : bit 255

15.2 Mmoire 256


15.3 Reprsentations usuelles des types simples 256

15.3.1Reprsentation des caractres : ASCII et UNICODE 257


15.3.2Reprsentation des entiers positifs : binaire 258
15.3.3Reprsentation des entiers relatifs : complment 2 259
15.3.4Reprsentation des nombres rels 262

15.4 Reprsentation des types composites : structures, objets,


tableaux 265
15.4.1Reprsentation des structures et des objets de type classe
15.4.2Reprsentation des tableaux 266

265

15.5 Oprations sur les rels - erreurs 267

15.5.1Addition - soustraction 267


15.5.2Multiplication - Division 267
15.5.3Erreurs dans les calculs sur les rels 267
15.5.4Erreurs lors des additions et des soustractions

ANNEXE 1

Installations 271
Installation de Java 271
Test de linstallation de java 271
Ajout de paquetages 272
Installation dEclipse 273
Ouverture dun projet Eclipse 273
Cration dune classe 274
Excution 275

ANNEXE 2

Paquetages pour les exercices 277


Entres-sorties clavier et fichiers textes 277
Listes 281
Ensembles 286

Module A.P.I

Algorithmique Programmation Imprative

268

Universit de Rennes 1

239

Introduction

CHAPITRE 1

Introduction

Objectif du cours algorithmique et programmation imprative


Le but de ce cours est lapprentissage des bases de lalgorithmique. Lalgorithmique est la
science des mthodes automatiques de calcul. Le terme calcul doit tre compris au sens
large, savoir la manipulation dinformations de toutes sortes. Ce module sintresse aux
algorithmes de style impratif, cest--dire construits laide dinstructions qui agissent
sur des donnes. Les instructions sont enchanes au moyens de structures de contrle
usuelles : conditionnelle, itration et appel de fonction. Dans la premire partie de ce cours,
les types des donnes manipules sont limits aux types primitifs simples (nombres entiers,
nombres rels, caractres, boolens), aux structures, aux chanes de caractres et aux
tableaux. Les outils plus sophistiqus permettant linvention de types de donnes abstraits
seront tudis dans une deuxime partie.
Lalgorithmique est non seulement une science, mais aussi une technique. Le but de ce cours
est de raliser effectivement des programmes. Pour cela il faut connatre un langage de programmation. Nous avons choisi Java pour de nombreuses raisons : cest un langage relativement simple qui permet cependant dillustrer tous les concepts modernes de programmation.
De plus cest un langage trs utilis par les professionnels et sa distribution est gratuite.

1.1

Algorithmes, langages de programmation, programmes


objectif : comprendre la notion lmentaire dalgorithme et avoir un aperu des diverses sortes de langages de programmation.

Un algorithme est la description des oprations qui permettent daccomplir une tche. Cette

tche peut tre un calcul, une transformation dinformation, voire un procd de fabrication.
Lusage dalgorithmes est en fait assez rpandu. Un modle de tricot, une recette de cuisine...
sont des algorithmes. Un algorithme doit tre une description prcise, mais pas ncessairement
exprim dans un langage prdfini. Tous les moyens dexpression sont acceptables : langue
naturelle, agrmente de mathmatiques, de croquis...
Un langage de programmation est un langage rigoureux, comprhensible par une machine, qui
permet dexprimer sans aucune ambigut des algorithmes.
Un programme est un algorithme exprim dans un langage de programmation.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

Algorithmes, langages de programmation, programmes

1.1.1

Langages de programmation
Il existe de nombreux langages de programmation qui se sont dvelopps au cours des ans en fonction des besoins, de lvolution des ordinateurs et des mthodes dans le domaine du gnie logiciel.

Les langages les plus rudimentaires sont les langages machines. Ils utilisent des instructions

directement excutes par lordinateur. Ils prsentent de nombreux inconvnients :


- ils sont difficiles utiliser,
- ils sont propres un type de machine,
- les programmes sont peu lisibles et comprhensibles par les personnes humaines.
Pour faciliter lcriture des programmes, on a dvelopp des langages volus :
- ils aident concevoir les algorithmes,
- ils sont indpendants des machines utilises,
- ils sont plus faciles comprendre et vrifier.
Pour pouvoir tre excuts sur une machine, ils ncessitent une phase de traduction par un compilateur.
On distingue deux grandes familles de langages volus :
- les langages dclaratifs qui rsolvent un problme en utilisant des proprits de sa solution,
- les langages impratifs qui rsolvent un problme en indiquant la succession des oprations
effectuer.
Ce cours est consacr aux langages impratifs. Cependant, pour les situer dans un cadre plus
gnral, nous donnerons quelques aperus sur les langages dclaratifs.

1.1.2

Algorithmes en langages dclaratifs


Les langages dclaratifs rsolvent un problme en utilisant certaines proprits auxquelles la solution doit satisfaire.
On peut distinguer deux types de langages dclaratifs :

les langages fonctionnels,


les langages relationnels.

1.1.2.1 Langages fonctionnels


Un programme en langage fonctionnel utilise des dfinitions et des applications de fonctions.
Lusage de dfinitions rcursives de fonctions permet de rsoudre une trs grande classe de problmes. Dans le cas dun programme fonctionnel pur, le rsultat dun programme est toujours le
rsultat de lvaluation dune fonction applique des paramtres.
Comme exemple simple on peut considrer le calcul de la somme des nombres entiers de 1 n :
1+2+3+...+n. Le rsultat est une fonction du nombre entier n. On peut appeler ce rsultat Sigma(n).
La fonction Sigma sexprime facilement de manire rcursive, cest--dire en sutilisant ellemme avec un paramtre plus simple que le sien :
En effet, on a Sigma(1) = 1
et pour n>1, Sigma(n) = 1+2+...+n-1 +n = Sigma(n-1) + n

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction

Dans un langage fonctionnel (hypothtique) cette fonction pourrait sexprimer ainsi :


fonction Sigma(n) = si n=1 alors 1 sinon Sigma(n-1)+n
Si on dsire calculer la somme des entiers de 1 4, il suffit dappliquer cette fonction Sigma avec
le nombre 4 en paramtre :
Sigma(4)
Le rsultat affich par lexcution est alors : 10
Il aura t calcul ainsi, par applications successives de la dfinition de la fonction Sigma :
Sigma(4) = si 4=1 alors 1 sinon Sigma(3)+4 = Sigma(3) +4
= si 3=1 alors 1 sinon Sigma(2)+3 +4 = Sigma(2) +3+4
= si 2=1 alors 1 sinon Sigma(1)+2 +3+4 = Sigma(1) + 2+3+4
= si 1=1 alors 1 sinon Sigma(0)+1 +2+3+4 = 1 +2+3+4 = 10
Parmi les langages fonctionnels les plus rputs, on peut citer : LISP, SCHEME, CAML.

1.1.2.2 Langages relationnels


Les langages relationnels sont bass sur la logique et les techniques de preuves. Un programme est
constitu de trois sortes de choses :

Une collection de faits, proprits ou relations affirmes concernant certaines donnes. En


reprenant lexemple du calcul de la somme des nombres entiers de 1 n, un tel fait serait :
Sigma de 1 vaut 1.
Des implications entre relations :
Sigma de n-1 vaut s et n > 1 implique que Sigma de n vaut s+n.
Une question rsoudre, par exemple : que vaut Sigma de 4 ?.
Dans un langage relationnel, ce programme pourrait scrire :
rgle 1 :
rgle 2 :
question :

Sigma(1,1).
Sigma(n,s+n) <- n>1 et Sigma(n-1,s)
Sigma(4,x)?

Lexcution du programme consiste, en utilisant les faits et les implications, chercher les solutions
sil en existe. Dans lexemple propos, lexcution trouve une solution : x=10.
Sigma(4,x)? la rgle 2 sapplique, et x vaut s+4 condition que Sigma(3,s)
Sigma(3,s)? la rgle 2 sapplique, et s vaut t+3 condition que Sigma(2,t)
Sigma(2,t)? la rgle 2 sapplique, et t vaut u+2 condition que Sigma(1,u)
Sigma(1,u)? la rgle 1 sapplique, et u vaut 1
donc t vaut 3, donc s vaut 6, donc x vaut 10
Le problme a une solution x=10.
Le plus connu des langages relationnels sappelle PROLOG.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

Algorithmes, langages de programmation, programmes

1.1.3

Algorithmes en langages impratifs


Pour effectuer un calcul en langage impratif, on indique une succession de transformations dtat
qui permet de passer dun tat initial un tat final contenant la solution.
Exemple simple dalgorithme :
calcul de la somme des nombres entiers de 1 n :
1+2+3+...+n.
Description de lalgorithme en style impratif :

utiliser une mmoire i initialise 1 (pour numrer les nombres 1, 2... n)


utiliser une mmoire somme initialise 0 (pour cumuler les valeurs successives de i)
tant que i est infrieur ou gal n : ajouter i somme, ajouter 1 i
le rsultat est la valeur de somme.
Exemple de programme :
En langage Java, voici la fonction Sigma qui calcule cette somme selon ce schma :
int Sigma(int n){
// prrequis
: n>=0
// rsultat
: la somme des nombres entiers de 1 n
int i=1; int somme=0;
while(i<=n) {
somme=somme+i;
i=i+1;
}
return somme;
}
Exemple dexcution de programme :
Voici comment se droulera lexcution de cette fonction pour n=4 :
lignes de programmes excutes

tat des variables

int i=1; int somme=0;

somme=0, i=1

while(i<=n){somme=somme+i; i=i+1;}

donc1<=4
somme=1, i=2

while(i<=n){somme=somme+i; i=i+1;}

donc2<=4
somme=3, i=3

while(i<=n){somme=somme+i; i=i+1;}

donc3<=4
somme=6, i=4

while(i<=n){somme=somme+i; i=i+1;}

donc4<=4
somme=10, i=5
5>4
donc somme et i inchangs

while(i<=n){}
return somme;

Module A.P.I

rsultat : 10

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction

1.2

Rigueur, syntaxe et smantique


objectif : comprendre la rigueur exige par lactivit de programmation.
La conception de programmes exige une rigueur absolue.
Pour assurer cette rigueur, les langages de programmation ont une syntaxe et une smantique
prcises :

La syntaxe est lensemble des rgles de construction que doivent respecter les textes des pro-

grammes.
La smantique est la signification des constructions du langage.

Un programme doit dabord respecter la syntaxe, sinon on ne peut lui attribuer aucune signification.
Dans lexemple prcdent :
while(i<=n) {somme=somme+i; i=i+1;}
utilise la syntaxe correcte du langage Java pour exprimer une rptition doprations.
La signification donne cette construction est (informellement) :
tant que i est infrieur ou gal n : ajouter i somme, ajouter 1 i
En revanche, la forme :
while(i<=n) {somme=somme+i i=i+1}
comporte une erreur de syntaxe : le langage exige un ; la fin de chaque instruction lmentaire.
Un programme peut tre correct au niveau syntaxique sans pour autant calculer ce que lon dsire. Il
y a alors une erreur smantique (ou erreur de conception).
Ainsi la forme :
while(i<n) {somme=somme+i; i=i+1;}
est syntaxiquement correcte, mais cela ne calcule pas ce que lon veut car cette version arrte le
cumul sur n-1 cause de lingalit stricte utilise dans le test i<n.
Les notions de syntaxe et de smantique se rencontrent dans tout langage. Comme exemple imag,
on peut considrer les phrases suivantes exprimes dans un langage de physique lmentaire :

lige plomb le : cette phrase est une erreur de syntaxe, on ne peut lui attribuer aucune signifi-

cation.
le lige flotte : cette phrase est syntaxiquement correcte. Elle signifie que le lige flotte (il est
moins dense que leau). De plus elle exprime une chose exacte. Elle est smantiquement correcte (dans la mesure o lexactitude est la valeur qui nous intresse).
le plomb flotte : cette phrase est syntaxiquement correcte. Elle signifie que le plomb flotte.
Elle exprime une chose inexacte. On peut considrer que cest une erreur smantique.
Les erreurs de syntaxe ne sont jamais graves car un compilateur les signale avant toute excution.
Les erreurs de conception sont plus difficiles corriger car elles ne se manifestent pas toujours et
elles peuvent ncessiter une phase de mise au point longue et fastidieuse.
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

Rsolution dun problme

1.3

Rsolution dun problme


objectif : comprendre la ncessit de bien analyser un problme avant de rdiger un programme cens le rsoudre

1.3.1

Phase danalyse et phase de programmation


Pour viter les erreurs de conception, on ne peut pas se lancer directement dans la programmation.
On distingue deux phases principales dans la rsolution dun problme, une fois celui-ci nonc
clairement dans un cahier des charges :

Lanalyse : elle consiste exprimer la rsolution du problme sous une forme la moins ambigu

possible comprhensible par un tre humain.


La programmation : elle exprime cette solution en termes dobjets informatiques et dinstructions dans un langage de programmation.
La dmarche pour rsoudre un problme peut sillustrer comme suit :
cahier des charges
analyse
algorithme
programmation

programme en langage volu


compilateur
programme excutable
excution par un ordinateur

entre de donnes

1.3.2

sortie de rsultats

Mthodes danalyse
Il existe plusieurs mthodes danalyse, plus ou moins adaptes la complexit et la nature des
problmes.

1.3.2.1 Application des mathmatiques :


Pour des problmes qui consistent calculer quelque chose, lanalyse consiste souvent utiliser
des rsultats connus de mathmatiques : quations, suites convergentes... De ce point de vue, lalgorithmique est une application directe des mathmatiques, limite ce qui peut se calculer effectivement (mathmatiques constructives).
1.3.2.2 Dcomposition fonctionnelle :
Pour des problmes peu complexes qui ne sappuient pas sur des mathmatiques traditionnelles, on
peut procder par dcomposition fonctionnelle : on dcompose le problme P en sous problmes

10

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction

P1, P2, P3... Pn que lon cherche rsoudre sparment. La dcomposition se poursuit jusqu
lobtention de sous-problmes trivialement rsolus. Les rsolutions des sous-problmes sont ralises par des procdures que lon regroupe gnralement par thmes dans des modules.
Comme exemple imag dune telle dcomposition, supposons que nous ayons dterminer litinraire routier pour aller de Rennes Quimper. Une premire analyse conduit :
(P1) Rennes Plormel
(P2) Plormel Lorient
(P3) Lorient Quimper
Le sous-problme P1 peut lui mme tre dcompos en :
(P11) Rennes Mordelles
(P12) Mordelles Pllan-le-Grand
(P13) Pllan-le-Grand Plormel
La dcomposition se poursuit plus ou moins loin. Tout dpend des possibilits du langage utilis et
de procdures dj existantes dans les bibliothques.
1.3.2.3 Analyse avec des objets :
Pour des problmes plus complexes, il faut utiliser une mthode danalyse par objets qui consiste
spcifier de faon abstraite les donnes manipules et les oprations quelles peuvent subir et
ensuite raliser ces abstractions au moyen des types de donnes offerts par le langage ou que lon
sait raliser.

1.3.3

Qualits dun programme


Les qualits essentielles dun programme sont en premier lieu dtre correct (faire ce que sa spcification a prvu) et en second lieu son efficacit (le faire rapidement et en utilisant le moins de ressources possibles).
Mais contrairement au simple exercice ralis en travaux pratiques qui est abandonn aussitt mis
au point, un produit logiciel industriel peut tre utilis pendant plusieurs annes. Au cours de ces
annes, lquipe informatique sera amene en assurer la maintenance pour ladapter aux nouvelles rglementations ou pour amliorer ses fonctionnalits.
Le temps pass maintenir un logiciel est souvent plus important que celui qui a t consacr son
dveloppement initial. Cest pourquoi, parmi les qualits dun logiciel, on privilgie tout particulirement la clart de son code car elle facilite la maintenance. Cette qualit est favorise en respectant des rgles de programmation dfinies pour un projet et en vitant les bricolages plus ou moins
gniaux comprhensibles de leur seul inventeur.

1.4

Exemple introductif
objectif : tre capable de concevoir des programmes trs simples par analogie avec un exemple.
Nous prendrons un exemple trs simple.
Cahier des charges : calculer la surface et le volume de plusieurs sphres de rayons donns.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

11

Exemple introductif

1.4.1

Analyse
Lanalyse est ici triviale : on applique des rsultats de mathmatiques. La surface et le volume des
sphres sont des rsultats bien connus : la surface est 4 rayon2 et le volume 4 rayon3/3.
Cependant, le problme pos consistant faire ces calculs pour plusieurs sphres, il convient de
dfinir deux fonctions aire et volume dpendant dun paramtre qui est le rayon.
aire : rel rel
aire(rayon) = 4 rayon2
volume : rel rel
volume(rayon) = 4 rayon3/3
Ces fonctions pourront alors tre sollicites avec diverses valeurs du rayon.
Important : si un calcul correspond une notion clairement identifie, comme cest le cas ici avec
les notions de surface de sphre et de volume de sphre il est recommand de le raliser sous
forme dune fonction, mme si ce calcul nest pas destin tre utilis plusieurs fois.

1.4.2

Rdaction du programme
Le problme prcdent peut tre rsolu en Java par le programme suivant :
class Sphere {

nom du programme

static final double PI = 3.141592;

dclaration de constante globale

static double aire(double r) { dfinition de la fonction aire


// rsultat
: laire dune sphre de rayon r
return 4*PI*r*r;
}
static double volume(double r) { dfinition de la fonction volume
// rsultat
: le volume dune sphre de rayon r
return 4*PI*r*r*r
/ 3;
}

public static void main(String[] arg) {


System.out.println(aire(4.2));
System.out.println (volume(4.2));
System.out.println (aire(2)));
System.out.println (volume(2));
}

procdure principale

Explication des termes du programme :


class Sphere
{ ...dfinit
} une classe appele Sphere qui regroupe tout le programme.
En Java, tout programme est une classe. Plus gnralement, un programme peut tre constitu de
plusieurs classes : la classe est lunit de programmation. Dans des exemples plus compliqus, les
classes permettront de regrouper des fonctions concernant un mme thme. Enfin, dans un style de
programmation par objets , les classes seront des modles dobjets qui reprsentent les choses
plus ou moins abstraites manipules par une application.

12

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction

Une classe comporte dans son intrieur des dclarations de donnes globales et des dfinitions de
fonctions.
Ici nous avons une dclaration de donne globale :
static final double PI = 3.141592;
Cette dclaration donne le nom PI la constante bien connue. Le vocable final indique quil
sagit dune constante, ce qui signifie quon na pas le droit de modifier la valeur associe au nom
PI. Les donnes manipules (constantes, variables, paramtres et rsultats de fonctions) ont un
type. Le type dune donne caractrise le domaine auquel appartient sa valeur. Ici le vocable double indique que la valeur de PI est un nombre rel de grande prcision1. Le vocable static
signifie que la constante PI existe ds le dbut dexcution du programme (cela peut sembler
curieux davoir le dire explicitement, mais cest ainsi)2.
Nous avons ensuite deux dfinitions de fonctions : aire et volume . Une dfinition de fonction est
compose du nom de la fonction suivie de ses paramtres formels entre parenthses. Le type du
rsultat de la fonction est indiqu devant le nom de la fonction et le type des paramtres est indiqu
devant chaque nom de paramtre.
Ainsi :
static double aire(double r)
signifie que la fonction aire a un paramtre de type double appel r et que son rsultat est de
type double . Le vocable static est obligatoire dans le cas dune simple fonction3.
Les textes compris entre // et la fin de la ligne sont des commentaires. Ils sont ignors par le
compilateur et servent documenter le programme. Ici,
// rsultat

: laire dune sphre de rayon r

est un commentaire de spcification, qui indique la fonction (mathmatique) ralise par cette
fonction (programme).
Le rsultat dune fonction est obtenu en excutant les instructions qui figurent dans le corps de la
fonction, entre accolades {return
4*PI*r*r;}
. Linstruction return expression calcule
lexpression et termine lexcution en donnant comme valeur le rsultat de ce calcul. Pour effectuer
les calculs, on dispose des oprateurs usuels, * pour la multiplication, / pour la division.
Pour quun programme soit directement excutable, il doit possder une procdure principale :
public static void main(String[] arg)
Cest sur cette procdure que commencera lexcution. Une procdure possde des paramtres et
est compose dinstructions, mais contrairement une fonction elle ne rend pas de rsultat. Cela est
indiqu par le vocable void devant le nom de la procdure.
En Java, la procdure principale doit sappeler main et doit avoir un paramtre de type
String[] (ce paramtre sert capter des informations transmises au lancement de lexcution,
mais on sen servira rarement). Une procdure ne rend pas de rsultat au sens fonctionnel du mot.
Autrement dit, tant donn une procdure P, son invocation ... P()... ne signifie aucune valeur. Pour
1. Le vocable double vient du terme double prcision qui dsigne cette sorte de reprsentation de nombres rels.
2. Java est un langage objets et les classes servent souvent programmer des modles dobjets : dans ce cas les donnes
dclares sans ce vocable static sont propres chaque objet cr selon ce modle.
3. Sans le vocable static la fonction serait considre comme une mthode dobjet destine sappliquer sur un objet
dont la classe serait le modle, ce qui nest pas le cas ici.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

13

Exemple introductif

tre utile, elle doit produire un effet. La notion deffet est primordiale pour un langage impratif,
alors que cette notion est quasiment inexistante pour un langage fonctionnel pur1. Les effets sont
des actions perceptibles sur lenvironnement du programme ou de la machine. Dans les cas simples,
les effets sont de nature informationnelle tel que afficher des informations sur lcran de lordinateur ou mmoriser des informations de faon rmanente dans des fichiers. Les effets peuvent galement tre de nature plus physique, tels que piloter un avion ou contrler le freinage dune voiture.
Dans lexemple, linstruction System.out.println(); produit un effet. Elle affiche sur
lcran le rsultat de lvaluation de son paramtre. Ainsi la procdure principale a pour effet dafficher successivement laire et le volume dune sphre de rayon 4.2 puis laire et le volume dune
sphre de rayon 2.

1.4.3

Compilation - excution
Pour utiliser ce programme, il faut dabord le compiler puis lancer son excution. Dans le cas de
Java, le programme prcdent doit tre plac dans un fichier appel Sphere.java . Avec un systme rudimentaire de dveloppement de programmes, la compilation se fait en tapant la
commande :
javac Sphere.java
Sil y a des erreurs de syntaxe, le compilateur javac (pour Java Compiler) les signale. Il faut dans
ce cas corriger ces erreurs et recommencer la compilation avant daller plus loin. Si le programme
est exempt derreur de syntaxe, le compilateur produit un programme excutable dans un fichier
Sphere.class . Le texte du programme en langage volu, Sphere.java , sappelle programme source. Le programme excutable rsultat de la compilation, Sphere.class , sappelle
programme objet ou encore code objet.
On peut alors lancer lexcution en tapant la commande : java Sphere
Lexcution du programme provoque laffichage suivant sur lcran :
221.671296
310.3398144
50.2656
33.5104
Sphere.java
class Sphere {
...
...
}

javac Sphere.java
compilation
excution
java Sphere

Sphere.class
010001101001
100100101001
011010110110
100100100101

221.671296
310.3398144
50.2656
33.5104

1. Le seul effet dun programme fonctionnel pur est laffichage du rsultat de son valuation.

14

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction

QCM 1.1
Un algorithme est :

1 - Un programme dordinateur rdig dans un langage de programmation prcis.


2 - Une recette automatisable (mcanisable) pour rsoudre un problme.
3 - Une thorie physique.
4 - Une fonction mathmatique trs lentement croissante.
QCM 1.2
Une faute de syntaxe est :

1 - Une erreur grave qui se produit pendant lexcution dun programme.


2 - Un non respect des rgles dcriture dun programme dans un langage de programmation

prcis.

3 - Une mauvaise mthode de rsolution dun problme.


4 - Bien plus grave et difficile corriger quune faute de smantique.
5 - Facile corriger car indique et localise par le compilateur.
QCM 1.3
On considre le programme suivant :
class TestFoisPi{
static final double PI=3.141592;
static double foisPi(int k) {
return k*PI;
}

public static void main(String[] arg) {


System.out.println(foisPi(10));
}

Ce programme :

1 - Affiche : 3.141592
2 - Affiche : 6.283184
3 - Affiche : 31.41592
4 - Naffiche rien.
5 - Est refus par le compilateur car prsentant une erreur de syntaxe.
3 - Retourne 31.41592

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

15

Exemple introductif

Exercice 1.1

Installation

objectif : prendre en main les outils de programmation - indispensable pour pouvoir aller plus
loin.
Raliser linstallation de lenvironnement de travail en suivant les indications de lannexe1 :

Installation de Java
Test de linstallation de Java
Accs aux paquetages pour les exercices
Installation dEclipse
Usage dEclipse.

Exercice 1.2

Un petit programme simple

objectif : comprendre le rle des diverses rubriques de lexemple du programme Sphere


En sinspirant du programme Sphere donn en exemple dans le chapitre prcdent, rdiger un
programme Parallelepipede qui calcule la surface et le volume de paralllpipdes rectangles de diverses dimensions. Le programme doit afficher la surface et le volume de paralllpipdes
de dimensions 1235 et 43410.

16

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction

Aide 1.2

Un petit programme simple

Il faut dfinir deux fonctions, aire et volume . Ces fonctions ont ici trois paramtres, les dimensions du paralllogramme dans les trois directions de lespace. Le programme total, que lon pourra
appeler Parallelepipede contiendra ces deux fonctions et la procdure principale main :
class Parallelepipede {
static double aire(double lx, double ly, double lz) {
// rsultat
: laire dun paralllpipde
// de dimensions lx, ly, lz
...
}
static double volume(double lx, double ly, double lz) {
// rsultat
: le volume dun paralllpipde
// de dimensions lx, ly, lz
...
}

public static void main(String[] arg) {


...
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

17

Exemple introductif

18

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction aux types de donnes

Introduction aux types de donnes

CHAPITRE 2

objectif : apprendre ce que sont les types de donnes et comprendre leur intrt.
Vous avez pu remarquer une diffrence dans les deux programmes donns en exemple au chapitre
prcdent :

le premier, qui calcule la somme des n premiers nombres entiers, utilise des donnes (i, n et
somme ) qualifies par le vocable int,
le second, qui calcule laire et le volume de sphres, utilise des donnes (PI, r, aire,
volume ) qualifies par le vocable double .

Les vocables int et double indique les types des donnes manipules. Pourquoi cette
diffrence ? Est-elle ncessaire, utile, arbitraire ?

2.1

Notion de type
objectif : comprendre ce quest un type de donne.
Les problmes traits par linformatique ne concernent pas uniquement des nombres. Ils peuvent
concerner des couleurs (bleu, vert, rouge...), des textes ou des choses encore plus exotiques comme
des personnes, des voitures, des avions... Chaque nature de donne est dfinie par un type.
Un type est caractris par :

un domaine de valeurs,
les oprations qui sont possibles sur ces valeurs.
Exemples :
nature

exemple de valeurs

exemple doprations

nombre entier

12

5+7

nombre rel

3.14

6.28/2.0

chane de caractres

"bonjour"

valeur logique (vrai ou faux)

false

type Java
int
double

"bon"+"jour"
i>0 && i<3

String
boolean

Un type sert spcifier quelle sortes de donnes sont acceptes comme paramtres et rendues en
rsultat dune opration. Par exemple, lopration daddition de nombres entiers, note +, admet
deux oprandes entiers (type int) et rend en rsultat une valeur entire (type int galement).
Autre exemple, lopration de comparaison dentiers, note >, admet deux oprandes de type
int et rend en rsultat une valeur de type boolean (x>y vaut true si x>y, false sinon).

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

19

Intrt des types

2.2

Intrt des types


objectif : montrer lintrt dtre oblig dindiquer le type les donnes et comment cette obligation devient un avantage en rendant les programmes plus lisibles et plus srs.

2.2.1

Cohrence des donnes


Le principal intrt des types est de permettre des vrifications de cohrence du programme avant
lexcution. Le compilateur vrifie que les paramtres des oprations sont de type convenable et que
le rsultat est utilis de faon convenable. Certes la vrification de cohrence des types ne garantit
pas quun programme est correct, cest--dire quil ralise ce que lon veut, mais elle rejette les programmes absurdes, sans quil soit besoin de les excuter pour sapercevoir de leur absurdit. Cela
acclre donc la mise au point des programmes en faisant faire cette vrification prliminaire au
compilateur.
On peut illustrer le rle des types en prenant un exemple imag. Considrons :

le type Solide , reprsentant les matires ayant une forme propre,


le type Liquide , reprsentant les matires tendant scouler,
et les oprations :
fondre(Solide aFondre) , manger(Solide aManger)
bouillir(Liquide aBouillir) , boire(Liquide aBoire)
Soient les dclarations suivantes :
Solide leBeurre; Solide leGruyere; Solide leCaoutchouc;
Liquide lHuile; Liquide lEau; Liquide leVin; Liquide leMazout;
Linstruction fondre(leVin) est refuse par le compilateur, car elle est absurde et on sen aperoit sans essayer dexcuter cette opration.
En revanche, les instructions boire(lHuile) , boire(leMazout) , fondre(leBeurre)
sont acceptes car les oprations mentionnes sont compatibles avec le type de leurs arguments.
Ceci ne signifie pas que ces instructions sont ncessairement correctes, par exemple
boire(leMazout) est peut-tre une erreur de programmation : lusage des types ne garantit pas
lexactitude des programmes mais il permet au compilateur de dtecter un grand nombre derreurs.
Exemple plus raliste :
12/"bonjour"

diviser un nombre par un texte na pas de sens,

double PI=3.1589;
(i<14)-PI
soustraire un nombre rel dune valeur logique est absurde.
Ces expressions seront refuses par le compilateur.

2.2.2

Ncessit technologique de reprsenter correctement linformation


Le langage machine diffrencie dj diverses sortes de donnes, par leurs reprsentations sur des
paquets de bits et par les oprations offertes : nombres entiers, nombres rels... Ces diffrences sont
invitables : les nombres entiers sont des quantits exactes, les nombres rels sont ncessairement de prcision limite...

20

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction aux types de donnes

2.2.3

Intrt technologique de dterminer les besoins en mmoire


Un troisime intrt des types, sans importance pour les aspects logiques des algorithmes, mais
important pour lefficacit des programmes, est de permettre au compilateur de savoir comment utiliser convenablement la mmoire de lordinateur. Une donne occupe dans la mmoire une place
qui dpend de sa nature. Par exemple un caractre est reprsent sur un mot de 8 ou 16 bits (un ou
deux octets), un nombre entier sur 32 bits, un rel en double prcision sur 64 bits. La connaissance
du type des donnes permet au compilateur dutiliser juste la mmoire ncessaire.

2.3

Panorama des diverses catgories de types


objectif : faire un tour dhorizon rapide sur les catgories de types qui seront tudis par la suite.

Le tableau suivant reprsente une classification des types offerts par la plupart des langages de
programmation :

scalaires

entiers

12

rels

12.0

caractres

'w

boolens

false

types primitifs
tableaux
types

composs
chanes
types programms

0
1
2
3

12
2
25
12

"coucou"

numrs

Couleur = {bleu,blanc,rouge}

structures

Personne = <nom,age>

classe

ex. <toto,19>

class Voiture {

...
void accelerer() {...}
void freiner() {...}

Un certain nombre de types sont offerts par le langage. On les appelle types primitifs. Parmi les
types primitifs on peut distinguer :

les types scalaires : nombres entiers, nombres rels, caractres, boolens...


les types composs, tableaux, chanes de caractres... qui reprsentent des collections de donnes. Un tableau de n entiers correspond peu prs lide mathmatique dun vecteur de n
composantes entires. Une chane de caractres est une suite finie de caractres, souvent utilise pour constituer des textes lisibles par un tre humain.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

21

Utilisations des types

Les langages offrent galement au programmeur le moyen de dfinir de nouveaux types, quon peut
appeler types programms :

Les plus simples des types programms sont les types numrs. Un type numr est une col-

lection finie de valeurs, sans autre proprit que lgalit. Un exemple de tel type est un ensemble de couleurs, {bleu, blanc, rouge}.
Une structure correspond peu prs lide mathmatique de produit cartsien de plusieurs
domaines. Par exemple la description dune personne se compose dun nom, qui est une chane
de caractres, et dun ge, qui est une valeur entire.
Dans un langage objets comme Java, un type programm peut se dfinir au moyen dune
classe. Une telle classe joue le rle de modle pour dcrire des objets de mme nature : on y
dcrit comment ils sont reprsents au moyen de types dj existants et on y programme les oprations auxquelles ils participent. Lexemple suggr dans le tableau concerne un type Voiture qui reprsente des voitures, dont ltat est caractris par une certaine vitesse et que lon
peut acclrer ou freiner.
Dans ce chapitre, nous nous bornerons prsenter les types primitifs scalaires. Les autres seront
vus ultrieurement, une fois matrise la rdaction de programmes simples.

2.4

Utilisations des types


objectif : savoir dclarer des donnes, connatre linfluence des types sur la signification des
symboles opratoires.

2.4.1

Dclarations
Une dclaration consiste donner un nom et indiquer le type dune donne ou dune fonction. En
Java, une dclaration de donne a une des formes suivantes :

Dclaration de donne non initialise :


type nom;
Une valeur sera attribue ultrieurement par une instruction de la forme :
nom = expression;
Exemple : int i;
... i=12;

Dclaration de donne initialise :


type nom = expression;
Exemple : int i=12;

Dclaration de fonction et de paramtres de fonction :


typeDuRsultat nomDeLaFonction(type nom,...) {...}
les paramtres de la fonction sont dclars dans les parenthses la suite du nom de la fonction,
sous la forme type nom. Ils ne sont jamais initialiss dans la dclaration, car leur valeurs sont
fournies aux endroits du programme qui appelle la fonction.
Exemple, une fonction qui rend en rsultat k :
static double kFoisPI(int k) {return k*3.141592;}

22

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction aux types de donnes

2.4.2

Exemples doprations sur des donnes de types primitifs

2.4.2.1 Oprations sur les nombres rels


Les oprations daddition (+), de soustraction (-), de multiplication (*) et de division (/) sont traditionnelles. Il faut cependant se mfier : les types offerts par les langages de programmation ne
sont que des approximations des nombres rels.
Considrons le programme suivant :
class Test {
public static void main(String[] arg) {
double x = 49.0;
System.out.println(x*(1.0/x)==1.0);
}
}
Lexpression x*(1.0/x)==1.0 teste si x /x est gal 1.
Pour x=49.0, le rsultat affich est false (faux), cause de la prcision limite de la reprsentation des nombres rels (type double ). Ainsi x multipli par 1/x ne donne pas toujours 1. Ce phnomne est similaire ce qui se passe en dcimal si on calcule 3 1/3 sur un nombre de chiffres
limit : on obtient 3 0,333333 = 0.999999 au lieu de 1.

2.4.2.2 Oprations sur les nombres entiers


Les opration daddition (+), de soustraction (-) et de multiplication (*) sont les oprations usuelles. La division sur les nombres entiers conduit deux oprations :

le quotient entier de a par b (not a/b),


le reste de la division de a par b (on dit galement a modulo b, not a%b).
Exemple :
14/3 vaut 4 et 14%3 vaut 2
car 14 divis par 3 donne un quotient entier gal 4 et il reste 2.

2.4.2.3 Oprations sur les boolens


Le type boolen, boolean en Java, possde deux valeurs, vrai (true) et faux (false ). Ces
valeurs servent principalement valuer des conditions qui servent dargument des prises de dcisions.
Les oprations de comparaisons numriques (infrieur <, suprieur >, infrieur ou gal <= , suprieur ou gal >= , gal == ) fournissent des rsultats boolens. Exemples :
3>5 vaut false , 5<=9 vaut true
Les oprations avec oprandes boolens sont le ET (&& ) le OU (||) et la ngation (!). Elles permettent dexprimer des conditions complexes. Exemple, pour tester si un entier k est compris entre
5 et 9, bornes incluses, on peut utiliser lexpression : (k>=5) && (k<=9)

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

23

Description des types scalaires

2.4.2.4 Types des expressions - conversions


Il faut tre attentif et rigoureux lorsquon ralise des oprations numriques, car le mme symbole
opratoire, +, -, *, /, dsigne des oprations diffrentes selon le type des oprandes. Les types des
oprandes dune opration se dduisent naturellement de la forme des expressions :

les notations numriques pour les nombres rels se distinguent par la prsence dun point ou

dune notation dite scientifique : 3.0, 12E-3 (pour 12 10-3),


les noms de donnes ont le type de leur dclaration :
avec double x=7;
... x/2.0
vaut 3.5 (division de nombre rels),
avec int x=7;
... x/2
vaut 3 (division de nombre entiers),
la sollicitation dune fonction a le type dclar de son rsultat :
kfoisPI(2) est un nombre rel, de type double , qui vaut 6.283182.

Il existe des oprations qui permettent de convertir des donnes dun type dans un autre type. Ces
oprations sappellent des conversions. Elles ralisent des correspondances usuelles entre domaines
de valeurs :

la conversion dun entier en rel fournit le rel qui reprsente la mme quantit,
la conversion dun rel en entier fournit la partie entire du nombre rel.
En Java une telle conversion se note : (type) expression
Exemples :
(int) 3.141592 vaut lentier 3, de type int
(double) 6 vaut le rel 6.0, de type double
En plus de ces rgles strictes, la plupart des langages ralisent implicitement la conversion des
entiers en rel dans les oprations notes +, -, *, / lorsque un des oprandes est explicitement rel.
Par exemple :
5.0/2 est quivalent 5.0/((double) 2) et vaut donc 2.5.

2.5

Description des types scalaires


objectif : prciser le comportement des types primitifs scalaires de Java (ce paragraphe peut
tre saut en premire lecture. Sy reporter au fur et mesure des besoins).

2.5.1

Types primitifs scalaires


En Java offre les types primitifs scalaires suivants :
domaine reprsent

24

types Java

nombres entiers

int, long,
short, byte

nombres rels

double, float

caractres

char

valeurs logiques

boolean

Module A.P.I

notations de valeurs
12

-4567

3.141592
'a'

'z'
true

Algorithmique Programmation Imprative

6.02E23
'?'

'\n'

false

Universit de Rennes 1

Introduction aux types de donnes

Il existe plusieurs types pour les nombres entiers : int, cods sur 32 bits (de -231 231-1), long,
cods sur 64 bits (de -263 263-1), short sur 16 bits (de -215 215-1) et byte sur 8 bits (de -128
127). Nous utiliserons gnralement le type int, bien quil ne permette pas de reprsenter des
entiers trs grands ( peu prs de -2 milliards +2 milliards).
Les valeurs entires sont notes classiquement, en dcimal.
De mme il existe deux types pour les nombres rels : float , cods sur 32 bits (nombres en simple prcision) et double , cods sur 64 bits (nombres en double prcision). Nous utiliserons le type
double , car le type float est vraiment peu prcis (mme pas lquivalent de 7 chiffres dcimaux
pour la mantisse).
Les valeurs relles sont notes avec un point pour sparer la partie entire de la partie fractionnaire.
La notation avec exposant 6.02E23 signifie 6.021023.
Les caractres sont reprsents par le type char. Les notations de valeurs utilisent lapostrophe :
'x' signifie le caractre x. Pour les caractres dont lcriture directe est impossible, on utilise une
notation particulire : '\n' signifie le passage la ligne, '\r' le retour en dbut de ligne, '\t'
une tabulation...

Les tableaux suivants rsument les oprateurs du langage. Pour chaque oprateur on a prcis son
profil, cest--dire le type de ses paramtres et de son rsultat. Certaines oprations et constantes
sont notes au moyen dun nom prfix par Math : Math.abs , Math.sqrt , Math.PI ... Ce
sont des fonctions et constantes dfinies dans la classe Math de la bibliothque standard de Java.
oprations avec arguments de type entier
notation

profil

exemple

rsultat

addition

int int int

4 + 12

16

soustraction

int int int

4 - 12

-8

multiplication

int int int

4 * 12

48

changement de signe

int int

- 12

-12

division entire

int int int

23 / 4

modulo (reste)

int int int

23 % 4

test dgalit

==

int int boolean

4 == 12

false

test dingalit

!=

int int boolean

4 != 12

true

>

int int boolean

4 > 12

false

>=

int int boolean

4 >= 4

true

<

int int boolean

4 < 4

false

<=

int int boolean

4 <= 4

true

test de supriorit
test suprieur ou gal
test dinfriorit
test infrieur ou gal
valeur absolue

Math.abs

int int

Module A.P.I. Algorithmique Programmation Imprative

Math.abs(-4)

Universit de Rennes 1

25

Description des types scalaires

oprations avec arguments de type rel


notation

profil

exemple

rsultat

addition

double double double

4.0 + 12.7

16.7

soustraction

double double double

4.5 - 12

-7.5

multiplication

double double double

4E8 * 12E-3

changement de
signe

double double

division

test de supriorit

48E5

- 12.0

-12.0

double double double

23.0 / 4.0

5.75

>

double double boolean

4.5 > 4.0

true

test dinfriorit

<

double double boolean

4.5 < 4.0

false

valeur absolue

Math.abs

double double

Math.abs(-4.5)

4.5

racine carre

Math.sqrt

double double

Math.sqrt(2.0)

1.414...

sinus

Math.sin

double double

Math.sin(0.0)

0.0

cosinus

Math.cos

double double

Math.cos(0.0)

1.0

tangente

Math.tan

double double

Math.tan(0.0)

0.0

logarithme nprien

Math.log

double double

Math.log(1.0)

0.0

exponentielle

Math.exp

double double

Math.exp(0.0)

1.0

constante

Math.PI

double

Math.PI

3.14159...

Math.E

double

Math.E

2.71828...

constante e

Remarque : dans la plupart des langages de programmation les types rels admettent les comparaisons faisant intervenir lgalit (=, , , ), mais il est prfrable de ne pas les utiliser car cest souvent un non-sens. cause des erreurs darrondi, lgalit de deux rsultats rels est gnralement le
fruit du hasard.

oprations avec arguments de type caractre


notation

profil

exemple

rsultat

test dgalit

==

char char boolean

'a' == 'w'

false

test dingalit

!=

char char boolean

'a'

true

>

char char boolean

'a' > 'w'

false

>=

char char boolean

'a' >= 'w'

false

int char boolean

'a' < 'w'

true

char char boolean

'a' <= 'a'

true

test de supriorit
test suprieur ou gal
test dinfriorit
test infrieur ou gal

<
<=

!= 'w'

Les caractres peuvent tre compars. Une relation dordre est dfinie sur les caractres et elle respecte lordre alphabtique pour les lettres 'A'... 'Z'... 'a'... 'z' et lordre numrique usuel pour
les chiffres '0'... '9'.

26

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction aux types de donnes

oprations avec arguments de type boolen


notation

profil

exemple

rsultat

ngation

boolean boolean

ou logique

boolean boolean boolean

true| false

||

boolean boolean boolean

true|| ???

&

boolean boolean boolean

false & true

et logique conditionnel

&&

boolean boolean boolean

false &&???

test dgalit

==

boolean boolean boolean

false==false

true

test dingalit

!=

boolean boolean boolean

false!=false

false

ou logique conditionnel
et logique

! false

true
true
true
false
false

Le ou simple, not |, value toujours ses deux arguments, alors que le ou conditionnel, not
||, nvalue pas le second argument si le premier est vrai (le premier argument dtermine le
rsultat vrai). De mme le et simple, &, value ses deux arguments, alors que le et conditionnel,
&&, nvalue pas le second argument si le premier est faux (le premier argument dtermine le
rsultat faux).
La diffrence est importante comme le montre lexemple suivant :
x>0

& (3/x)<12

provoque une erreur lexcution si x vaut 0 car (3/0)<12 est valu et provoque lerreur tentative de division par 0.
En revanche
x>0

&& (3/x)<12

ne provoque pas derreur si x vaut 0 car x>0 est faux et le total vaut donc faux.

2.5.2

Types numrs
Java ( partir de la version 1.5) offre les types numrs. Un tel type possde un ensemble fini de
valeurs dsignes par autant didentificateurs. On peut par exemple dfinir le type Couleur
comme un type numr possdant 3 valeurs notes bleu, blanc et rouge :
enum Couleur {bleu, blanc, rouge};
Les dclarations des donnes dun type numr utilisent le nom du type, et les dsignations de
valeurs se font au moyen de la notation nomDuType.identificateur. Le seul oprateur dfini sur un
type numr est le test dgalit, not == .
Exemple :

Couleur

milieuDuDrapeau=Couleur.blanc; dfinit la couleur du milieu du drapeau.


Lexpression de comparaison milieuDuDrapeau==Couleur.blanc vaut true,
Lexpression de comparaison milieuDuDrapeau==Couleur.rouge vaut false .

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

27

Description des types scalaires

QCM 2.1
Soit lexpression :

2>=3 || 12%2==0

1 - Cest une faute de syntaxe.


2 - Son rsultat est de type int.
3 - Son rsultat est de type boolean .
4 - Son rsultat est 6.
5- Son rsultat est vrai.
6 - Son rsultat est faux.
QCM 2.2
Soit lexpression :

0<2<5

1 - Cest une faute de syntaxe.


2 - Son rsultat est de type int.
3 - Son rsultat est de type boolean .
4 - Son rsultat est 6.
5- Son rsultat est vrai.
6 - Son rsultat est faux.
QCM 2.3
Soit lexpression :

2/5

1 - Cest une faute de syntaxe.


2 - Son rsultat est de type int.
3 - Son rsultat est de type double .
4 - Son rsultat est 0.2.
5- Son rsultat est 0.
6 - Cela provoque une erreur pendant lexcution.
QCM 2.4
Soit lexpression :

(double) (2/5)

1 - Cest une faute de syntaxe.


2 - Son rsultat est de type int.
3 - Son rsultat est de type double .
4 - Son rsultat est 0.2.
5- Son rsultat est 0.0.
6 - Cela provoque une erreur pendant lexcution.

28

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction aux types de donnes

QCM 2.5
Soit lexpression :

((double) 2)/5

1 - Cest une faute de syntaxe.


2 - Son rsultat est de type int.
3 - Son rsultat est de type double .
4 - Son rsultat est 0.2.
5- Son rsultat est 0.0.
6 - Cela provoque une erreur pendant lexcution.
QCM 2.6

1 - Une donne de type int peut tre ngative.


2 - Une donne de type int peut valoir 1000000000 (un milliard).
3 - Une donne de type int peut valoir 100000000000 (cent milliards).
4 - Une donne de type long peut valoir 1000000000 (un milliard).
5- Une donne de type long peut valoir 100000000000 (cent milliards).
6 - Une donne de type int peut valoir 0.5 (un demi).
7 - Une donne de type long peut valoir 0.5 (un demi).
QCM 2.7

1 - Une donne de type double peut tre ngative.


2 - Une donne de type double peut valoir 1000000000 (un milliard).
3 - Une donne de type double peut valoir 100000000000 (cent milliards).
4 - Une donne de type double peut valoir 0.5 (un demi).
5 - Une donne de type double peut tre nulle.
QCM 2.8
Considrons le type Couleur et les fonctions estChaude et estFoide ainsi dfinies :
enum Couleur{rouge,orange,jaune,vert,bleu,violet};
static boolean estChaude(Couleur c){
return c==Couleur.rouge
|| c==Couleur.orange
}

|| c==Couleur.jaun

static boolean estFroide(Couleur c){


return
!estChaude(c);
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

29

Description des types scalaires

1 - Lexpression estChaude(Couleur verte) est une erreur de syntaxe.


2 - Lexpression estChaude(Couleur.verte) vaut true (vrai).
3 - Lexpression estChaude(Couleur.verte) vaut false (faux).
4 - Lexpression estChaude(Couleur.verte) vaut 0 (entier nul).
5 - Lexpression estFroide(Couleur.verte) vaut false (faux).
6 - Lexpression estFroide(Couleur.verte) vaut true (vrai).
7 - Lexpression estChaude(Couleur.verte) est une erreur de syntaxe.
8- Lexpression estChaude(Couleur.rouge) est une erreur de syntaxe.
9 - Lexpression estChaude(Couleur.indigot) est une erreur de syntaxe.
10 - Lexpression estChaude(Couleur.rouge) vaut true (vrai).

30

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction aux types de donnes

Exercice 2.1

Expressions, priorit des oprateurs

objectif : se familiariser avec les types des oprandes et du rsultat dune expression, comprendre les erreurs de syntaxes provoques par le non respect des types.
Lexpression 2+3*4 peut se comprendre de deux manires :
(2+3)*4 qui vaut 20 ou 2+(3*4) qui vaut 14
Pour viter les ambiguts, les oprateurs en Java possdent des priorits. Lopration la plus prioritaire est effectue en premier.
Oprateurs par ordre de priorit dcroissante :
- (moins unaire)
*, /,%(multiplication, division, reste)
+, - (addition, soustraction)
==,!=, <, >, <=, >= (comparaisons)
&& (et logique)
|| (ou logique)
Lexpression 2+3*4 se comprend donc 2+(3*4) et vaut 14.
En cas dgalit des priorits, le calcul a lieu de gauche droite.
Ainsi, 1-4-5 se comprend ((1- 4)-5) et vaut -8.
Si lon veut contrler ces priorits, il est ncessaire de parenthser.
Par exemple, pour obtenir 20, il faut crire (2+3)*4 .

On considre les expressions suivantes :


(1) 4-3*2-1

(2) -4-5

(4) 2/3>0

(5) 4-5-3==2*-5/2+1

(6) 3>2 && 1<4

(7) 6<7&&7<5+3

(3) 5-2<4*2
(8)

6<7<5+3

1 - Parenthsage explicite :

indiquer les quivalents totalement parenthss de ces expressions,


indiquer celles qui sont des erreurs de syntaxe (fautes de type),
indiquer le rsultat de celles qui sont correctes.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

31

Description des types scalaires

2 - Vrification :
Une faon simple de vrifier ce qui prcde consiste rdiger un programme de test (une simple
procdure main). Voici le modle, pour vrifier que 2+3*4 est quivalent 2+(3*4) :
class TestParenthesage {
public static void main(String[] arg) {
System.out.print"2+3*4 = ");
System.out.println(2+3*4);
System.out.print("quivalent
: 2+(3*4) = ");
System.out.println(2+(3*4));}
}
Ce modle montre une faon systmatique de raliser un programme de test :

System.out.println("2+3*4 = ") imprime le texte de la formule tester,


System.out.print(2+3*4) imprime le rsultat de lexcution la formule.
Ceci affiche : 2+3*4 = 14
Certaines des formules proposes sont des erreurs de syntaxe. Essayer de comprendre le message
derreur du compilateur, puis inhiber les lignes qui sont des erreurs en les transformant en commentaire. Il suffit pour cela de placer // en dbut de ligne :
// System.out.print(2*+/4-);

Exercice 2.2

Moyenne de trois nombres

objectif : savoir choisir les types des donnes manipules, faire des conversions si ncessaire.
Premire partie
Rdiger un programme TestMoyenne qui calcule la moyenne de 3 nombres entiers (de type int)
pour les triplets de nombres suivants :
3, 1, 1
-12, 2, 12
4, 6,7
Attention : les rsultats du calcul de la moyenne sont des nombres rels (de type double ).

32

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Introduction aux types de donnes

Deuxime partie
En Java, la procdure principale a pour en-tte :
public static void main(String[] arg)
Le paramtre arg est un tableau de chanes de caractres (type String[] ). Ce paramtre est un
tableau dont les lments valent les chanes de caractres frappes la suite du nom du programme
lors de son lancement. Ceci permet de passer des paramtres au programme lors de son lancement.
Si par exemple on lance le programme par la commande :
java TestMoyenne 12 53 6
arg[0] vaut "12", arg[1] vaut "53" et arg[2] vaut "6".
Les paramtres sont des chanes de caractres. Pour obtenir les nombres rels correspondant, il faut
utiliser la fonction de conversion offerte par la bibliothque Java :
Integer.valueOf( s)
qui rend en rsultat le nombre entier reprsent par la chane de caractres s.
Exemple : Integer.valueOf( "12") vaut le nombre entier 12.
Rajouter la procdure principale de TestMoyenne le calcul et laffichage de la moyenne de 3
nombres frapps la suite de la commande de lancement.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

33

Description des types scalaires

Aide 2.2
La fonction moyenneDeTroisNombres a trois paramtres, les trois nombres entiers dont il faut
calculer la moyenne.
class TestMoyenneDeTroisNombres {
static double moyenneDeTroisNombres(int x, int y, int z){
// rsultat
: moyenne de x, y et z
...
}

public static void main(String[] args) {


System.out.print("moyenne de 3,1,1 = ");
System.out.println(moyenneDeTroisNombres(3,1,1));
...
...
}

Se mfier de la division entire : si on divise la somme de trois entiers par 3, la division effectue est
la division entire, ce qui nest pas la moyenne. Penser faire correctement une conversion dentier
en rel de type double .

34

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Dclarations de donnes, fonctions, expressions, instructions

CHAPITRE 3

Dclarations de donnes, fonctions,


expressions, instructions

objectif : connatre prcisment les diverses rubriques dun programme, les diverses sortes de
donnes, les expressions et les instructions lmentaires.

Les types spcifient la nature des donnes manipules par les programmes. Nous allons voir maintenant ce qui permet de manipuler les donnes :

les dclarations de donnes qui identifient les donnes,


les instructions. Nous nous limiterons dans ce chapitre la dfinition de fonctions, lappel de
fonctions, la squentialit et laffichage dun rsultat.

3.1

Structure de programmes simples


Un programme simple possde la structure suivante :
class nom du programme {
dclarations de donnes globales
dfinitions de fonctions
}

procdure principale

Les dclarations de donnes globales permettent de dfinir des donnes qui seront utilisables
depuis tout le texte du programme, par opposition aux paramtres et donnes locales des diverses
fonctions du programme qui ne sont utilisables que depuis le texte de ces fonctions.
Commentaires :
Pour faciliter la comprhension dun programme, des commentaires peuvent tre utiles et parfois
ncessaires. Un commentaire est une explication destine aux lecteurs humains, ignore par la
machine (le compilateur).

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

35

Dclarations de donnes

En Java il en existe de deux formes :


// texte commentaire jusqu la fin de la ligne
et
/* commentaires
sur un nombre quelconque
de lignes */
La premire forme est la plus agrable pour les commentaires qui spcifient ce que font les programmes.
La deuxime forme est souvent utilise temporairement, en phase de mise au point de programmes,
pour inhiber la prise en compte de certaines parties du programme.

3.2

3.2.1

Dclarations de donnes

Les diverses sortes de donnes


Un programme est constitu dinstructions qui agissent sur des donnes. Dune faon trs
gnrale une donne est quelque chose qui :

joue un certain rle,


a un nom (on dit aussi un identificateur),
possde une valeur dun certain type.
En plus de leur type, les donnes ont divers qualificatifs :

Une donne peut tre globale ou locale :


- Une donne globale est utilisable depuis toutes les fonctions de la classe qui constitue le programme. Une dclaration de donne globale apparat au premier niveau de la classe, en dehors
de toute fonction. De plus, une donne globale peut tre statique ou non statique. Une donne
statique est cre ds le dbut dexcution du programme. Dans le cas de programmes simples,
les donnes globales seront toujours statiques. Elles sont prcdes du vocable static
- Une donne locale nest utilisable que depuis le texte de la fonction o elles est dclare.

Une donne peut tre une variable ou une constante :


- La valeur associe une variable peut tre modifie en cours dexcution, au moyen dune instruction daffectation.
- La valeur associe une constante est fixe une fois pour toute lendroit de sa dclaration.
Une dclaration de constante est prcde du vocable final .
Voici la forme gnrale des diverses dclarations de donnes que lon peut faire dans un
programme :

36

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Dclarations de donnes, fonctions, expressions, instructions

class leProgramme {
...
static final type nom = valeur ; constante globale
static type nom ; variable globale
...
static void ppp(...) { dfinition de fonction
final type nom = valeur ; constante locale la procdure ppp
type nom; variable locale la fonction ppp
...
}

public static void main(String[] z) { procdure principale


final type nom = valeur ; constante locale la procdure principale
...
}

Exemple :
class leProgramme {
...
static final double graviteTerrestre = 9.81; constante globale
static final int nombreDeCartes = 32; constante globale
...

public static void main(String[] z) {


final int ageDuCapitaine = 53; constante locale
int score; variable locale
...
}

Remarques :

Une dclaration locale na jamais le vocable static .


lusage des variables sera prcis ultrieurement. Leur principal intrt concerne la ralisation
des itrations.
Le besoin dutiliser des variables statiques globales est trs rare : il faut lviter.

3.2.2

Intrt des dclarations de constantes


Les dclarations de constantes permettent, dans les instructions des programmes, dutiliser les noms
de ces constantes la place des notations de valeurs. Ceci prsente plusieurs avantages :

Une plus grande lisibilit du programme. En effet un programme nest pas seulement destin
une machine, il doit aussi pouvoir tre compris par des lecteurs humains, pour faciliter sa mise
au point et ultrieurement sa maintenance, cest--dire permettre des modifications dues aux
changements des besoins des utilisateurs du programme.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

37

Dclarations de donnes

Une facilit de modificationen centralisant en un seul endroit la notation de valeur. Si la cons-

tante doit tre modifie, dans une version ultrieure, seule la dclaration de constante est
modifier.

public static void main(String[] z) {


final int ageDuCapitaine = 53; un seul endroit modifier
...
pour changer lge du capitaine
... ageDuCapitaine
... ageDuCapitaine
...
...
... ageDuCapitaine
...
}

Les dclarations de constantes peuvent tre utilises galement pour nommer des rsultats
intermdiaires et ainsi viter des expressions trop grosses qui pourraient nuire la lisibilit :
soit un rectangle dfini par les coordonnes de son coin suprieur droit (x1,y1) et de son coin
infrieur gauche (x2,y2 ). On peut calculer la dimension de sa diagonale par :
final double d = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
x1,y1
d

h
x2,y2

mais on prfrera peut-tre utiliser les notion intermdiaires de hauteur et de largeur :


final double h = x1-x2;
final double l = y1-y2;
final double d = Math.sqrt(h*h+l*l);

Enfin, les dclarations de constantes servent calculer une seule fois une valeur qui doit tre

utilise plusieurs reprises :


soit par exemple le calcul de la surface de base et du volume dun paralllpipde. Le volume
est lui-mme obtenu en multipliant la surface de base par la hauteur. On veut calculer une fois la
surface de base :

class Parallelepipede {
public static void main(String[] arg) {
int largeur=...; int longueur=...; int hauteur=...;
final int surfaceDeBase=largeur*longueur;

38

System.out.print("surface de base = ");


System.out.println(surfaceDeBase);
System.out.print("volume = ");
System.out.println(surfaceDeBase*hauteur);

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Dclarations de donnes, fonctions, expressions, instructions

3.3

Dfinitions de fonctions
objectif : savoir dfinir une fonction, distinguer les fonctions qui ralisent des fonctions au
sens mathmatique et les procdures qui ont des effets.

Une dfinition de fonctiona la forme suivante1 :


static typeDuRsultat nomDeLaFonction(type1 param1, type2 param2...) {
dclarations locales et instructions
}
Le type du rsultat est indiqu devant le nom de la fonction, le type et le nom de chaque paramtre
sont indiqus entre parenthses la suite du nom de la fonction. Ces paramtres sont appels paramtres formels.
Si une fonction na pas de paramtre, il faut tout de mme mettre les parenthses :
static typeDuRsultat nomDeLaFonction() {...}
En langage impratif, il existe des fonctions qui ne rendent aucun rsultat. On a lhabitude dappeler procdure une telle fonction. Dans ce cas on utilise le vocable void la place du type du
rsultat :
static void nomDeLaProcdure(type1 param1, type2 param2...) {
dclarations locales et instructions
}

Une fonction est une collection identifie dinstructions qui fait quelque chose. Une fonction est
destine tre appele. Cela consiste donner des valeurs, appeles paramtres effectifs, la place
des paramtres formels et excuter les instructions de la fonction en utilisant ces paramtres effectifs.
On peut distinguer trois catgories de fonctions :
les fonctions pures, les procdures et les fonctions impures
:

Une fonction pure ralise une fonction au sens mathmatique du mot. Elle calcule une valeur
dun certain type et la rend en rsultat, sans plus. Par exemple la fonction sigma3 qui calcule la
somme de 3 nombres entiers :
static int sigma3(int a, int b, int c) {
// rsultat
: la somme de a, b et c
return a+b+c;
}

1. Le vocable static signifie ici que la fonction est simple, que ce nest pas une mthode dobjet (voir plus loin).

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

39

Dfinitions de fonctions

Un appel de cette fonction pourra avoir la forme sigma(3,5,4) qui a pour rsultat 12. Un tel
appel na pas deffet, il signifie une valeur. Pour que lappel serve quelque chose, il faut ncessairement que la valeur soit utilise, soit en donnant cette valeur une variable du programme :
int x = sigma3(3,5,4);... donne la valeur 12 x,
soit en lutilisant pour calculer le paramtre effectif dun oprateur ou dun autre appel de
fonction :
... System.out.print(sigma3(3,5,4)+2));... affiche 14 sur lcran.

Une procdure ne rend pas de rsultat. On lutilise pour les effets quelle produit. Parmi les
effets on peut distinguer les interactions avec lenvironnement, par exemple :
des affichages sur cran,
des acquisitions de donnes introduites par un utilisateur du programme,
des lectures et critures de fichiers,
des actions sur des moteurs, des vannes...
des changements dtat de certains objets du programme.
On peut prendre comme exemple la procdure afficheSommeDe3Nombres qui affiche sur
lcran la somme de trois nombres :
static void afficheSommeDe3Nombres(int a, int b, int c) {
// effet
: affiche la somme de a, b et c
System.out.print(a+b+c);
}
Un appel possible de cette procdure est afficheSommeDe3Nombres(3,5,4) qui a pour
effet dafficher 12 sur lcran :
12

Une fonction impure


rend un rsultat, comme une fonction pure, mais elle ne ralise pas une

simple fonction mathmatique. Elle peut avoir un effet, comme une procdure, ou consulter
lenvironnement. On peut prendre pour exemple la fonction lectureNombreEntier qui
rend en rsultat le nombre entier frapp au clavier par lutilisateur du programme :
static int lectureNombreEntier() { ... lit un entier...}

Cette fonction attend que lutilisateur frappe une suite de chiffres au clavier, et sil frappe par
exemple 176, elle rend lentier 176 en rsultat. Ceci ne ralise de toute vidence pas une
fonction au sens mathmatique, puisque bien que sans paramtre elle peut fournir des rsultats
diffrents chaque appel.
Dans la suite, on utilisera le terme gnral de fonction pour dsigner indiffremment une procdure, une fonction pure ou une fonction impure.
En Java, un mme nom de fonction peut tre utilis pour des fonctions diffrentes, condition que
ces fonctions diffrent par les types ou le nombre de paramtres. Lusage de ces fonctions nest pas
40

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Dclarations de donnes, fonctions, expressions, instructions

ambigu, car on voit trs bien, et le compilateur aussi, quels sont les types et le nombre des paramtres effectifs dun appel et cela dtermine quelle fonction est utilise. Par exemple, on peut utiliser
le mme nom, maximum , pour la fonction qui rend en rsultat le plus grand parmi deux caractres
(selon lordre alphabtique) :
static char maximum(char c1, char c2) {
if (c1>c2) {return c1;} else {return c2;}
(si c1>c2 alors le rsultat est c1, sinon le rsultat est c2)
}
et pour la fonction qui rend en rsultat le plus grand de deux nombres entiers :
static int maximum(int i, int j) {
if (i>j) {return i;} else {return j;}
}
On appelle cela la surcharge des noms de fonction (overloading en anglais). Il ne faut pas en
abuser, mais cest souvent pratique. Cela vite dinventer des noms diffrents pour des fonctions
similaires sur des domaines diffrents. Dailleurs les oprateurs du langage utilisent cette pratique,
puisque par exemple les symboles +, -, *, /, >, <, ==... servent dsigner les oprations usuelles,
et cependant diffrentes, sur les entiers, les rels...

3.4

Commentaires de spcification dune fonction


objectif : savoir commenter sans ambigut les services rendus par une fonction. Apprendre
distinguer rsultats et effets. Savoir ennoncer ce que a rend ou ce que a fait sans
la moindre allusion comment cest fait.

Une fonction est destine des utilisateurs (ventuellement soi-mme). Il est essentiel de documenter correctement ce quelle offre. Ceci se fait au moyen dun commentaire de spcification. Un tel
commentaire doit noncer avec rigueur et sans ambigut ce que ralise la fonction. Il doit se limiter
dire ce que ralise la fonction et non pas comment elle le ralise car cela ne concerne pas lutilisation de la fonction.
Il est bon que la prsentation des commentaires de spcification soit toujours la mme, cela amliore la lisibilit des programmes. Voici ce que nous proposons :

On le place juste aprs len-tte de la fonction, en dbut du corps.


Il est compos de trois rubriques, prsentes uniquement si elles sont ncessaires :
Prrequis : indique les conditions dans lesquelles lappel est permis. Cest une formule logique
concernant les paramtres et ventuellement lenvironnement extrieur et ltat de certaines
variables globales.
Effet : indique les effets sur lextrieur ou sur certaines variables globales.
Rsultat : indique quelle valeur est rendue en rsultat, en fonction des paramtres et ventuellement de lextrieur et de certaines variables globales.
Une fonction pure na pas de rubrique effet (elle noffre quun rsultat), une procdure na pas de
rubrique rsultat (elle noffre quun effet) et une fonction impure a la fois une rubrique effet
et une rubrique rsultat.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

41

Instructions et expressions

Exemple de commentaires de spcification dune fonction :


static double racineCarre(double x){
// prrequis
: x>=0
// rsultat
: la racine carre de x
...
}
Exemple de commentaires de spcification dune procdure :
static void affichePersonne(String n, String p, int a){
// effet
: affiche les caractristiques dune personne de nom n
// prnom
p, ge a sous la forme "nom : n , prnom
: p , ge
System.out.print("nom
: "); System.out.print(n);
System.out.print("prnom
: "); System.out.print(p);
System.out.print("ge
: "); System.out.print(a);
System.out.print(" ans");
}

3.5

Instructions et expressions
objectif : savoir utiliser les instructions lmentaires.

3.5.1

Squences dinstructions
Les instructions figurent dans ce quon appelle le corps des fonctions. En Java, les instructions lmentaires sont systmatiquement dlimites droite par un ;. Une instruction indique quoi faire
lordinateur. La combinaison la plus simple est la combinaison squentielle dinstructions : les
instructions places les unes la suite des autres sont excutes lune aprs lautre. Lors dun appel
de la fonction, la suite dinstructions ci-aprs est excute dans lordre (1) puis (2) puis (3) :
{

System.out.println(235); (1)
System.out.println(9+6); (2)
System.out.println(12); (3)

Lexcution de ces instructions affiche donc successivement 235 puis 15 puis 12.

235
15
12

42

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

: a a

Dclarations de donnes, fonctions, expressions, instructions

3.5.2

Instructions daffichage lcran


En Java, les instructions daffichage lcran sont :
et

void System.out.print( diversType x)


void System.out.println( diversType x)

La premire forme, print , affiche simplement x, et la seconde, println , provoque en plus un


passage au dbut de la ligne suivante aprs laffichage de x.
Ce sont des procdures de la bibliothque standard de Java. Elles admettent divers types pour le
paramtre x. Il existe une procdure dimpression pour chaque type primitif scalaire. Limpression
est faite selon le format usuel : notation dcimale pour les entiers, notation avec point et exposant
pour les rels, true et false pour les boolens...
Il existe galement des procdures daffichage dun texte, cest--dire dune chane de caractres de
longueur quelconque :
void System.out.print(String texte)
void System.out.println(String texte)
Le type chane de caractres, String , sera vu plus loin. Pour afficher des messages il suffit de
savoir quune valeur de type chane de caractres se note au moyen de la chane elle-mme entre
doubles apostrophes, comme par exemple "coucou" .
Voici un exemple de programme qui utilise laffichage de donnes de divers types :
class TestAffichage {
public static void main(String[] arg) {
System.out.print("la constante PI
affichage
: "); dun texte
System.out.println(Math.PI); affichage dun rel
System.out.print("la constante E
affichage
: "); dun texte
System.out.println(Math.E); affichage dun rel
System.out.print("le nombre de nains
affichage
: "); dun texte
System.out.println(7); affichage dun entier
System.out.print("la 1re lettre de lalphabet
un texte
: ");
System.out.println(a); affichage dun caractre
System.out.print("3 est-il plus grand que 8
un?texte
: ");
System.out.println(3>8); affichage dun boolen
}
}
et voici son effet sur lcran :
la constante PI
: 3.141592653589793
la constante E
: 2.718281828459045
le nombre de nains
: 7
la 1re lettre de lalphabet
: a
3 est-il plus grand que 8
? : false

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

43

Instructions et expressions

3.5.3

Expression de concatnation de chanes de caractres


Les affichages se font en dfinitive toujours sous forme de chanes de caractres. Si les fonctions
daffichage telles que System.out.print admettent tous les types primitifs du langage, cest
par lintermdiaire de conversions standard en chanes de caractres que cet affichage peut se
faire.
Les chanes de caractre, type String en Java, peuvent tre labores par calcul, notamment grace
lopration de concatnation. La concatnation de deux chanes c1 et c2 est la chane qui commence comme c1 et se termine par c2. Cette opration est note + en Java (comme laddition, mais
cest sans ambigut).
Ainsi :
"tant va l" + "a chruche leau"
donne en rsultat "tant va la chruche leau"
De plus, si un des oprandes du symbole opratoire + est une chane de caractres, le symbole + est
compris (par le compilateur) comme signifiant la concatnation et lautre oprande est converti en
chane de caractres selon la conversion standard. Ainsi on peut concatner une chane de caractre
avec tout type primitif.
Exemple :
int k = 12;
String s = "il y a " + k + "oeufs dans le panier";
La chane s vaut "il y a 12 oeufs dans le panier" .
En rgle gnrale, il vaut mieux viter de rdiger des procdures qui affichent. Pour rpondre aux
besoins de communications dun programme avec lextrieurs, est bien prfrable de rdiger des
fonctions qui rendent des chanes de caractre en rsultat. Ceci est trs facile raliser grace la
concatnation.
Une telle fonction estdusage plus gnral : si on veut raliser un affichage, il suffit dafficher le
rsultat de la fonction dans une procdure de niveau suprieur (dans le main par exemple), mais si
on veut on en faire autre chose que de lafficher.

3.5.4

Instruction de retour de fonction


Pour rendre son rsultat, une fonction dispose de linstruction de retour dont la forme gnrale est :
return expression;
Exemple, fonction qui rend en rsultat la moyenne de trois nombres :
static int moyenneDe3Nombres(int i, int j, int k) {
return (i+j+k)/3;}
}
Lexpression doit tre du type du rsultat de la fonction. Cette instruction provoque deux choses :

elle value lexpression et donne sa valeur comme rsultat de la fonction,


elle termine lexcution de la fonction.

44

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Dclarations de donnes, fonctions, expressions, instructions

Il peut y avoir plusieurs instructions de retour dans le corps dune fonction, comme par exemple :
static int maximum(int i, int j) {
if (i>j) {return i;} else {return j;}
}
si i est plus grand que j, cest linstruction return
iqui est excute et termine en rendant en
rsultat la valeur de i, sinon cest linstruction return
jqui est excute et termine en rendant
en rsultat la valeur de j.
On peut ne pas aimer cette multitude dinstructions return dissmines dans le corps dune fonction. Pour faciliter la mise au point et la maintenance des programmes, on peut prfrer une seule
instruction return la fin du texte de la fonction. Pour cela il suffit de donner un nom au rsultat
au moyen dune variable temporaire1, par exemple resul et de terminer la fonction par linstruction return resul . Voici la fonction prcdente programme selon ce principe :
static int maximum(int i, int j) {
int resul;
if (i>j) {resul=i;} else {resul=j;}
return resul;
}
Les procdures, sans rsultat, peuvent galement utiliser une instruction return sans expression
accompagnatrice :
return

Le seul effet de cette instruction est de terminer lexcution de la procdure. Pour des raisons de
lisibilit et de maintenance, il vaut mieux viter cette instruction, sauf dans des cas trs simples, et
laisser se terminer la procdure par la fin de son texte, auquel cas linstruction return est inutile.

3.5.5

Expressions

3.5.5.1 Ce quest une expression


Un expression sert exprimer le calcul dune valeur. Une expression nest pas une instruction,
elle apparat dans une instruction pour spcifier une valeur qui intervient dans linstruction. Nous
venons de voir un exemple avec linstruction return : dans return
a+b+c;cest return
a+b+c; en entier qui est une instruction, a+b+c nest quune expression dont lvaluation,
savoir la somme de a, b et c, dtermine la valeur rendue en rsultat par ce return .
Les expressions sont construites de faon classique, laide des oprateurs du langage, dappels de
fonctions, de notations de valeurs, de constantes et de variables, en utilisant les parenthses pour
construire des expressions aussi compliques que lon veut en combinant des expressions plus simples. Voici quelques exemples dexpressions, accompagnes de leur type et de leur valeur :
12 lentier 12
nombreDeCartes lentier 32
12+4 la somme de 12 et de 4, savoir lentier 16
maximum(4,maximum(8,2)) le maximum de 4, 8 et 2, savoir 8
Comme le montrent ces exemples, certaines expressions peuvent tre trs simples : une notation de
valeur ou un nom de constante ou de variable constitue une expression.

1. Lutilisation des variables sera vue plus loin. Dans cet exemple la variable ne sert qu nommer un rsultat de calcul pour
sen servir en un autre endroit que celui o il est calcul.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

45

Instructions et expressions

3.5.5.2 O se rencontrent les expressions


Les expressions se rencontrent essentiellement en deux endroits :

dans les rendus de rsultat de fonction : return expression;


dans les appels de fonction, pour spcifier la valeur des paramtres effectifs : ... maxi

mum(expression1, expression2) ... par exemple maximum(12+3,14) qui vaut 15.

3.5.5.3 Usage des parenthses


Dans une expression complique, lordre dvaluation des oprations peut tre entirement contrl
grce aux parenthses :
15-(3+2) svalue en 10
(15-3)+2 svalue en 14
On peut se dispenser de certaines parenthses. Les oprateurs sont classs par priorit selon lordre
suivant, en commenant par les plus prioritaires :

- (moins unaire), ! (ngation),


* (multiplication), / (division),
+ (addition), - (soustraction)
>, <, >=, <=, ==, != (comparaisons)
&, && (et)
|, || (ou)
En labsence de parenthses, les oprateurs sont valus selon leur ordre de priorit et en cas dgalit de priorit ils sont valus en sassociant gauche (cest--dire de gauche droite). Par
exemple : 15-3*6+2 svalue comme (15-(3*6))+2 cest- dire en -1.
Conseil : pour viter les erreurs dues un mauvais usage des priorits, il vaut mieux mettre des
parenthses.

3.5.5.4 Diffrence essentielle entre impratif et fonctionnel


Pour bien apprhender la programmation imprative, il est essentiel de bien percevoir la diffrence
entre expressions et instructions. Une grande partie de la puissance mais aussi de la difficult des
langages impratifs vient de ce quils combinent partout deux notions trs diffrentes : les expressions qui ont une valeur et les instructions qui agissent. Dans la vie courante et le langage courant,
on rencontre galement ces deux notions. Voici une expression : le bus numro 12, cest une
valeur qui en soi nindique aucune action, ce nest pas une instruction. Pour construire une instruction, il faut gnralement rajouter un verbe daction : prend le bus numro 12 ou conduit le bus
numro 12 sont des instructions.
Cest la diffrence essentielle entre langages fonctionnels et langages impratifs : un langage fonctionnel est un langage uniquement compos dexpressions, cest un langage sans instructions. Un
langage fonctionnel pur ne permet de rien faire, car le faire nest pas dans ses notions de base.
Un langage fonctionnel ne permet que dnoncer des valeurs (ce qui nest dj pas si mal, car ces
valeurs peuvent tre trs difficiles calculer). Pour rendre un langage fonctionnel utilisable, soit on
lui rajoute autour un systme interactif dvaluation qui, au cours du temps, lit des expressions,
les value et affiche leurs rsultats, soit on renonce laspect purement fonctionnel en intgrant
au langage, de faon toujours plus ou moins scabreuse, des moyens dinteraction avec lenvironnement dexcution, des entres-sorties par exemple.

46

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Dclarations de donnes, fonctions, expressions, instructions

3.5.6

Appel de procdure - Appel de fonction


Une instruction dappel de procdure provoque lexcution des instructions de cette procdure. La
forme gnrale est :
nomDeLaProcdure(expression1, expression2...);
Les expressions sont values, leurs valeurs sont attribues aux paramtres formels de la procdure,
puis les instructions de la procdure sont excutes. Lorsque la procdure se termine, lexcution se
poursuit la suite de cette instruction dappel. Voici un exemple dappels de procdures :
class TestAppelDeProcedure {
Dfinition de procdure
static void afficheSurfaceDeCarre(double cote) {
System.out.print("la surface du carr de ct "); (p1)
System.out.print(cote); (p2)
System.out.print(" vaut "); (p3)
System.out.println(cote*cote); (p4)
}

Programme principal qui appelle la procdure


public static void main(String[] arg) {
afficheSurfaceDeCarre(2.0); (a1)
afficheSurfaceDeCarre(3.0); (a2)
}

Lexcution commence par la procdure principale, main.


La premire instruction excute (a1) est un appel de la procdure afficheSurfaceDeCarre
avec 2.0 pour paramtre effectif. Ceci a pour effet dexcuter les instructions (p1), (p2), (p3), (p4) du
corps de la procdure qui affichent la surface du carr de ct 2.0 vaut 4.0.
Lexcution retourne dans la procdure principale.
Linstruction (a2) est excute. Cest un nouvel appel de la procdure afficheSurfaceDeCarre avec 3.0 pour paramtre effectif. Ceci a pour effet dexcuter les instructions (p1), (p2), (p3),
(p4) qui affichent la surface du carr de ct 3.0 vaut 9.0.
On appelle trace dexcution la squence temporelle des excutions dinstructions.
Le schma suivant montre la trace dexcution de lexemple prcdent et son effet sur lcran.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

47

Instructions et expressions

trace dexcution
main

afficheSurfaceDeCarre

(a1)

(p1)
(p2)
(p3)

la surface du carr de ct 2.0 vaut 4.0


la surface du carr de ct 3.0 vaut 9.0

(p4)

temps
(a2)

(p1)
(p2)
(p3)
(p4)

Pour un appel de fonction il se passe peu prs la mme chose. Cependant un appel de fonction
nest pas une instruction, cest une expression qui vaut le rsultat de la fonction. Il faut donc faire
apparatre lappel en tant quexpression dans une instruction qui exploite ce rsultat. Lexemple suivant illustre lappel de fonction. Cest un programme qui provoque un affichage identique au prcdent, mais les actions dcriture sont dans la procdure principale main, la fonction
surfaceDeCarre ne faisant que calculer et rendre en rsultat la surface dun carr.
class TestAppelDeFonction {
Dfinition de fonction
static double surfaceDeCarre(double cote) {
return cote*cote; (p1)
}

48

Programme principal qui appelle la fonction


public static void main(String[] arg) {
System.out.print("la
surface
du carr
System.out.println(surfaceDeCarre(2.0)); (a2)
System.out.print("la
surface
du carr
System.out.println(surfaceDeCarre(3.0)); (a4)
}

Module A.P.I

Algorithmique Programmation Imprative

de ct

2.0
(a1) vaut

");

de ct

2.0
(a3) vaut

");

Universit de Rennes 1

Dclarations de donnes, fonctions, expressions, instructions

La trace dexcution de ce programme est illustre ci-dessous :


trace dexcution
main

surfaceDeCarre

(a1)
la surface du carr de ct 2.0 vaut 4.0
la surface du carr de ct 3.0 vaut 9.0

(a2)

(p1)
temps

(a3)
(a4)

(p1)

On remarquera ici une petite difficult dans le dessin : lappel de fonction surfaceDeCarre(2.0) est au sein mme de linstruction (a2). Pendant que la fonction est excute, linstruction (a2) nest pas termine. Lorsque la fonction se termine, le flot de contrle revient dans
linstruction (a2) pour la terminer (faire limpression de la valeur retourne ici).

3.5.7

Variables et instruction daffectation


Contrairement une constante, une variable peut prendre plusieurs valeurs successives pendant
lexcution du programme. Une dclaration de variable se prsente sous la forme :
type nom; ou bien type nom = valeurInitiale;
Le changement de valeur dune variable se fait au moyen dune instruction daffectation. En Java,
la forme gnrale de cette instruction est :
variable = expression ;
Le rsultat de lexpression doit tre du type de la variable. Une variable doit tre initialise, cest
dire recevoir une valeur par affectation, avant dtre utilise. Le compilateur Java le vrifie et refuse
tout programme dans lequel une variable risque de ne pas tre initialise avant son utilisation.
Exemples :
int k=2;
k=12;
k=9;

... k

... k
... k

...
utilisation de k ayant pour valeur 2

...
utilisation de k ayant pour valeur 12
...
utilisation de k ayant pour valeur 9

Remarque : le type nest indiqu quune seule fois, lendroit de dclaration de la variable.
En toute rigueur, lusage des variables nest ncessaire que pour raliser des itrations (voir plus
loin). Mais le langage ninterdit pas de les utiliser dans dautres circonstances, l o des constantes
auraient suffi. Voici par exemple trois faons de calculer le maximum de trois nombres a, b et c en

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

49

Instructions et expressions

disposant de la fonction max qui rend en rsultat le maximum de deux nombres :

version 1 : sans intermdiaire : int maxABC = max(max(a,b),c);


version 2 : avec une constante pour nommer le rsultat intermdiaire :
final int maxAB = max(a,b); int maxABC = max(maxAB,c);
version 3 : en utilisant une variable pour calculer progressivement le rsultat :
int maxABC = max(a,b); int maxABC = max(maxABC,c);
cette dernire version est sans doute la moins lgante des trois.

3.5.8

Instructions de lecture du clavier


Les donnes dun programme sont rarement fixes au moment de la rdaction du programme. Elles
sont gnralement fournies au moment de lexcution du programme, par exemple introduites au
clavier.
Le captage des donnes par le programme est fait par une opration de lecture. En Java, une opration de lecture se prsente comme un appel de fonction. Cette fonction attend que lutilisateur introduise des donnes et rend en rsultat les donnes introduites. La lecture se prsente donc comme
une expression.
Les donnes introduites peuvent tre de divers types, cest pourquoi il y a une fonction de lecture
pour chaque type de donnes susceptibles dtre lues. Nous utiliserons les fonctions suivantes1 :
int Lecture.unEntier() rend en rsultat lentier frapp au clavier
double Lecture.unReel() rend en rsultat le nombre rel frapp au clavier
char Lecture.unCar() rend en rsultat le caractre frapp au clavier
String Lecture.chaine() rend en rsultat la chane des caractres frapps au clavier
Lutilisateur est cens introduire les donnes selon les formats usuels. Except pour la lecture dun
caractre, les donnes sont introduites sous forme dune suite de caractres de longueur variable.
Des dlimiteurs doivent donc tre dfinis pour sparer les donnes. Les sparateurs choisis sont
lespace et le passage la ligne (' ', '\r' ou '\n').
Voici par exemple une squence dinstructions qui affecte aux variables i, c, x et message respectivement un entier, un caractre, un rel et une chane :
int i = Lecture.unEntier();
char c = Lecture.unCar();
double x = Lecture.unReel();
String message = Lecture.chaine();
Lorsque cette squence est excute, si lutilisateur frappe le texte suivant au clavier :
12

a6.02E23

coucou

i prend la valeur 12, c le caractre 'a', x la valeur 6.02 1023 et message la valeur "coucou" .

1. Ces fonctions ne sont pas les fonctions standards de Java pour lire des donnes au clavier. Ce sont des fonctions plus faciles utiliser introduites pour ce cours. Leurs dfinitions en termes des fonctions standards de Java sont donnes en annexe.

50

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Dclarations de donnes, fonctions, expressions, instructions

Pour disposer de ces fonctions de lecture, il faut placer en premire ligne du fichier source du programme la directive :
import es.*;
Exemple : le programme suivant calcule et affiche le volume dun paralllpipde rectangle dont la
largeur, la longueur et la hauteur sont fournies au clavier.
import es.*;
class Parallelepipede {
public static void main(String[] arg) {
final
final
final
final

int
int
int
int

largeur = Lecture.unEntier();
longueur = Lecture.unEntier();
hauteur = Lecture.unEntier();
surfaceDeBase=largeur*longueur;

System.out.print("surface de base = ");


System.out.println(surfaceDeBase);
System.out.print("volume = ");
System.out.println(surfaceDeBase*hauteur);

Comme le montre cet exemple, des constantes peuvent tre initialises par des lectures. Pour allger
le texte, on utilise parfois des variables (abusivement, en omettant le vocable final ) :
int largeur = Lecture.unEntier();

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

51

Instructions et expressions

QCM 3.1

On considre le programme suivant :

import es.*;
class TestLectures{
public static void main(String[] z){
int x = 12;
String s = "coucou";
x = Lecture.unEntier();
s = Lecture.chaine();
System.out.print("x="+x); System.out.println(" s="+s);
}
}
Au moment de lexcution, lutilisateur entre au clavier : 56 toto

1 - Il ne se passe rien et le programme termine son excution.


2 - Il ne se passe rien et le programme ne se termine pas.
3 - Il y a une erreur pendant lexcution.
4 - Le programme affiche : x=56 s=toto
5 - Le programme affiche : x=12 s=coucou
Au moment de lexcution, lutilisateur entre au clavier : tutu toto

6 - Il ne se passe rien et le programme termine son excution.


7 - Il ne se passe rien et le programme ne se termine pas.
8 - Il y a une erreur pendant lexcution.
9 - Le programme affiche : x=tutu s=toto
10 - Le programme affiche : x=12 s=coucou
Au moment de lexcution, lutilisateur entre au clavier : 56 95

11 - Il ne se passe rien et le programme termine son excution.


12 - Il ne se passe rien et le programme ne se termine pas.
13 - Il y a une erreur pendant lexcution.
14 - Le programme affiche : x=56 s=95
15 - Le programme affiche : x=12 s=coucou
Au moment de lexcution, lutilisateur nentre rien au clavier.

16 - Il ne se passe rien et le programme termine son excution.


17 - Il ne se passe rien et le programme ne se termine pas.
18 - Il y a une erreur pendant lexcution.
19 - Le programme affiche : x=56 s=95
20 - Le programme affiche : x= s=

52

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Dclarations de donnes, fonctions, expressions, instructions

QCM 3.2
On considre le programme suivant :
class TestFonctions{
static int x;
static int plusUn(int k){
// ???
return k+1;
}
static void affichePlusUn(int x){
// ???
System.out.print(x+1);
}
static void ajouteUnAX(){
// ???
x=x+1;
}

public static void main(String[] z){


x=12;
System.out.print("x="+x);
System.out.print(" 6+1="); affichePlusUn(6);
System.out.print(" x="+x); ajouteUnAX();
System.out.print(" x="+x);
System.out.println(" 8+1="+plusUn(8));
}

1 - Commentaire de spcification de plusUn : //


2 - Commentaire de spcification de plusUn : //
3 - Commentaire de spcification de plusUn : //
4 - Commentaire de spcification de plusUn : //

rsultat

: k+1

effet

: ajoute 1 k

effet

: affiche k+1

effet

: ajoute 1 x

5 - Commentaire de spcification de affichePlusUn


6 - Commentaire de spcification de affichePlusUn
7 - Commentaire de spcification de affichePlusUn
8 - Commentaire de spcification de affichePlusUn

: // rsultat
: // effet

: ajoute 1 k

: // effet

: affiche x+1

: // effet

: ajoute 1 x

9 - Commentaire de spcification de ajouteUnAX : // rsultat


10 - Commentaire de spcification de ajouteUnAX : // effet
11 - Commentaire de spcification de ajouteUnAX : // effet
Module A.P.I. Algorithmique Programmation Imprative

: x+1

: x+1
: ajoute 1 x
: affiche x+1

Universit de Rennes 1

53

Instructions et expressions

12 - La premire instruction du main, x=12; , affecte la variable globale x.


13 - La premire instruction du main, x=12; , donne la valeur 12 au paramtre x de la fonction

affichePlusUn .

14 - Linstruction du main affichePlusUn(6);


15 - Linstruction du main affichePlusUn(6);
16 - Le programme affiche x=12
17 - Le programme affiche x=12
18 - Le programme affiche x=12

54

Module A.P.I

affiche 7.
donne la valeur 7 la variable globale x.

6+1=7 x=7 x=8 8+1=9 .


6+1=7 x=12 x=13 8+1=9 .
6+1=7 x=13 x=14 8+1=9 .

Algorithmique Programmation Imprative

Universit de Rennes 1

Dclarations de donnes, fonctions, expressions, instructions

Exercice 3.1

Dfinition et appel de fonctions : surface dune couronne

objectif : savoir dfinir, combiner et utiliser des fonctions.


On dsire calculer la surface de couronnes dlimites par les cercles concentriques de rayons r1 et
r2. Les rayons sont quelconques : r1 peut tre suprieur, gal ou infrieur r2 :

r2
r1

r1
r2

Pour cela on dcide de dfinir les fonctions :

surfaceCercle : surface dun cercle de rayon donn,


surfaceCouronne : surface dune couronne dlimite par deux cercles de rayon donns.
Une surface ne doit pas tre un nombre ngatif. Pour assurer cela, la bibliothque standard de Java
offre la fonction :
double Math.abs(double x) : rsultat, la valeur absolue de x (x si x0, -x sinon)
On pourra galement utiliser avec profit la constante Math.PI offerte par la bibliothque.
Rdiger un programme TestCouronne qui affiche la surface de la couronne dlimite par

des cercles de rayons r1=2,3 et r2=4,1,


des cercles de rayons r1=4,1 et r2=2,3,
des cercles dont les rayons sont lus au clavier.

Exercice 3.2

Instructions de lecture et daffichage

objectif : savoir lire depuis le clavier des donnes de divers types


Les chanes de caractres sont de type String en Java. Par exemple voici comment se dclare et
se lit une variable destine contenir le nom dune personne :
String nom = Lecture.chaine();
Rdiger un programme qui lit au clavier :

lanne en cours (un nombre entier, par exemple 2006),


le nom et le prnom dune personne (des chanes de caractres),
lanne de naissance de cette personne (un nombre entier),
et affiche un texte de la forme :
Monsieur prnom nom, vous avez ge ans
o prnom, nom et ge sont le nom, le prnom et lge lanne en cours de la personne.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

55

Instructions et expressions

56

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Nommage, portes didentification, dures de vie, modularit

CHAPITRE 4

Nommage, portes didentification,


dures de vie, modularit

objectif : savoir o et comment donner des noms aux lments dun programme, quand naissent et meurent les donnes.
Le but de ce chapitre est dapporter des prcisions sur trois aspects importants :

Le nommage : quelle occasion on doit donner un nom une chose (constante, variable, fonc-

tion...) et comment on nomme les choses.


Les portes didentification: comment les noms sont associs aux choses.
Les dures de vie : quand naissent et meurent les choses pendant lexcution.

4.1

Nommage
Les noms qui apparaissent dans les programmes pour dsigner les choses sappellent des identificateurs. Ils doivent obir quelques rgles simples :

commencer par une lettre et se poursuivre par des lettres ou des chiffres,
ne pas tre identique un vocable du langage tel que, en Java :
class , int, boolean , if, else, while ...
A ces rgles de syntaxe sajoutent quelques conventions et conseils :

Choisir les noms les plus signifiants possibles : des noms bien choisis compltent, voire rempla-

cent avantageusement les commentaires. Il ne faut pas hsiter utiliser des mots longs si ncessaire. Par exemple :
final int graviteTerrestre = 9.81;
Respecter les normes communment admises, surtout si elles ont fait leur preuve. La norme la
plus rpandue actuellement est :
Les noms de classes commencent par une majuscule :
class TestMoyenne
Les noms de donnes et de fonctions commencent par une minuscule :
String prenom;
int maximum(int i, int j){...}
Pour les noms composs, les sparations se font au moyen de majuscules :
ageDuCapitaine
moyenneDeTroisNombres

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

57

Portes didentification

4.2

Portes didentification
Le schma suivant illustre la structure dun programme simple dot de diverses dclarations de
donnes :
class TestMachin {
static int v;
static double c;

dclarations de donnes globales

static int f(int p) {


int cc;
dclarations de donnes locales
int x;
instructions qui utilisent x ... cc ... p ... v ... c
}
static void g(int p) {
dclarations de donnes locales
char x;
instructions qui utilisent x ... p ... v ... c
}
public static void main(String[] arg) {
double y;
dclarations de donnes locales
instructions qui utilisent y ... v ... c
}

Dans ce schma, on a utilis les mmes noms pour dsigner des choses diffrentes :

une variable de la fonction f est appele x,


une variable de la fonction g est galement appele x.
Il ny a pas de confusion possible grce la rgle de porte des identifications: un identificateur
introduit par une dclaration de donne lintrieur dune fonction dsigne cette donne seulement
dans le corps de cette fonction. Ainsi, dans le corps de f, x dsigne la variable de type int dclare
dans f, alors que dans le corps de g, x dsigne la variable de type char dclare dans g. Dans la
procdure principale, x ne dsignerait rien, et son emploi serait une erreur de syntaxe.
Il en est de mme des identifications des paramtres des fonctions. Dans lexemple, nous avons utilis le mme identificateur p pour dsigner le paramtre de f et celui de g. Ces deux paramtres ne
sont bien videmment pas la mme chose.
En revanche, lidentificateur v dsigne, partout dans le programme, la variable dclare
static int v;
au niveau global du programme (en dehors de toute fonction). Il en est de mme de lidentificateur

58

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Nommage, portes didentification, dures de vie, modularit

c qui dsigne partout la mme donne et des identificateurs de fonctions f et g qui sont utilisables
partout pour dsigner la fonction f et la procdure g.
Une donne locale et une donne globale peuvent porter le mme nom. En cas dhomonymie, le
nom dsigne la donne locale. Il vaut mieux viter de telles homonymies car elles sont source
derreurs.

4.3

Dures de vie
Les donnes dclares au niveau global du programme, en dehors de toute fonction, et dotes du
vocable static , telle que static
int dans
v lexemple, sappellent des variables globales.
Elles existent pendant toute lexcution du programme. On dit que leur dure de vie stend du
dbut la fin de lexcution du programme. Plus gnralement cest ce que signifie le vocable
static : qui existe en un seul exemplaire pendant toute lexcution du programme1.
Les variables dclares au sein dune fonction, telle que int x dans la fonction f, char x dans
la fonction g ou encore double
y
dans la procdure principale main, sappellent des variables
locales. Elles existent uniquement pendant une excution de la fonction dans laquelle elles sont
dclares. Leur dure de vie stend du dbut la fin de lexcution de cette procdure : elles sont
cres au moment de lappel de la fonction et sont dtruites au retour.
Ceci signifie notamment quune variable locale na pas de valeur dtermine au dbut de lexcution dune fonction, alors quune variable globale a toujours une valeur dtermine, celle attribue
par sa dernire affectation.
Comme exemple simple dusage de variables globales, on peut considrer le programme suivant qui
imprime un texte avec un numro de ligne en dbut de chaque ligne :
class Imprime LaCigaleEtLaFourmi {
static int numeroDeLigne;

// pour numroter les lignes

static void imprimeLigne(String ligne) {


System.out.print(\n);
System.out.print(numeroDeLigne);
System.out.print("
"); System.out.print(ligne);
numeroDeLigne=numeroDeLigne+1;
}

public static void main(String[] arg) {


numeroDeLigne=1;
imprimeLigne("La cigale ayant chant");
imprimeLigne("Tout lt");
imprimeLigne("Se trouva fort dpourvue");
imprimeLigne("Quand la bise fut venue.");
}

La variable numeroDeLigne est une variable globale. Elle est accessible depuis le programme
principal qui linitialise 1, et par la procdure imprimeLigne qui imprime sa valeur en dbut de

1. Le vocable static qualifie donc les choses les plus simples, qui existent tout le temps, en correspondance directe
avec le texte de leur dclaration. Les choses plus compliques telles que les variables locales de fonctions ou bien les
composants dobjets (voir plus loin propos des classes modles dobjets) dont la dure de vie est gale la dure dexcution dun appel de fonction ou lexistence dun objet, ne ncessitent aucun vocable particulier.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

59

Identification temporaire - blocs dinstructions

ligne et lincrmente de manire y trouver une valeur augmente de 1 lors de sa prochaine excution. Ce programme imprime :
1
2
3
4

La cigale ayant chant


Tout lt
Se trouva fort dpourvue
Quand la bise fut venue.

Il ne faut pas abuser de lusage de variables globales. Il est assez rare den avoir besoin. Il ne faut
surtout pas les utiliser pour transmettre des arguments une fonction : les paramtres sont faits pour
cela. Lexemple prcdent est la limite dun bon usage des variables globales. On peut la place
utiliser un paramtre supplmentaire numeroDeLigne pour indiquer le numro de ligne la procdure imprimeLigne :
class ImprimeLaCigaleEtLaFourmi {
static void imprimeLigne(int numeroDeLigne, String ligne) {
System.out.print("\n");
System.out.print(numeroDeLigne);
System.out.print("
"); System.out.print(ligne);
}

4.4

public static void main(String[] arg) {


imprimeLigne(1,"La cigale ayant chant");
imprimeLigne(2,"Tout lt");
imprimeLigne(3,"Se trouva fort dpourvue");
imprimeLigne(4,"Quand la bise fut venue.");
}

Identification temporaire - blocs dinstructions


On est souvent amen, au sein dune fonction, nommer un rsultat intermdiaire. On a besoin de
le faire si ce rsultat intermdiaire doit tre utilis en plusieurs endroits du texte de la fonction. La
syntaxe des expressions est suffisante si le rsultat est simplement utilis en un endroit. Lexemple
suivant montre un calcul o une expression suffit et un calcul o une identification dun rsultat
intermdiaire est utile :
calcul de (sin(a+b)+sin(a-b))/ (sin(a+b)-sin(a-b))

calcul de (a+b)/(c-d)
resultat=(a+b)/(c-d)

double sPlus=sin(a+b);
double sMoins=sin(a-b);
resultat=(sPlus+sMoins)/(sPlus-sMoins);

expression sous forme darbre


resultat

expression sous
forme de treillis

resultat
+
sPlus

Module A.P.I

Algorithmique Programmation Imprative

sin

sin

60

/
sMoins
v

Universit de Rennes 1

Nommage, portes didentification, dures de vie, modularit

Si les rsultats intermdiaires ne sont utiliss quen un seul endroit, cest--dire si le calcul total
sexprime sous forme dun arbre doprateurs et doprandes, on peut mettre directement son
expression lendroit o il est utilis, en tant que sous-expression. Cest le cas de lexemple de gauche.
Si un rsultat intermdiaire est utilis en plusieurs endroits, on a intrt, et cest mme parfois
ncessaire, de nommer ce rsultat intermdiaire et dlaborer le calcul en plusieurs expressions distinctes. Cest le cas de lexemple de droite. Le calcul total ne sexprime pas sous forme dun arbre
mais sous forme dun treillis doprateurs et doprandes.
Les langages de programmation permettent de dclarer des variables locales pour nommer ces
rsultats intermdiaires. Il faut dclarer ces variables le plus prs possible des endroits dutilisation, et ne pas polluer le programme en les dclarant nimporte o. Il faut galement, si cest possible, leur donner une valeur lendroit de leur dclaration. Ce sont des variables locales et leur
dure de vie est donc le temps dexcution de la fonction.
Rien ninterdit bien sr de nommer un rsultat intermdiaire dans le cas o il nest utilis quen un
endroit. Cela peut mme tre plus clair, en vitant une expression peu lisible car trop longue ou en
mettant en valeur ce rsultat intermdiaire en le nommant de faon judicieuse. Un tel nommage
remplace avantageusement certains commentaires. Mais il ne faut pas abuser des identifications
inutiles. Des identifications intempestives attirent lattention sur ce qui ne le mrite pas et dgradent
la lisibilit en ne profitant pas du pouvoir expressif des expressions.
Java permet de placer des dclarations peu prs nimporte o dans le corps des fonctions : il faut
en profiter.
Blocs dinstructions
De nombreux langages, Java en particulier, possdent en plus une notion de bloc dinstructions. Il
sagit simplement dun regroupement dinstructions que lon peut accompagner de dclarations.
Ces dclarations sont alors locales au bloc : dune part une variable ou constante ainsi dclare a
une dure de vie gale la dure dexcution du bloc et dautre part lidentification introduite par la
dclaration a une porte limite ce bloc.
... dbut de bloc entier k, rel x... instructions qui peuvent utiliser k et x... fin de bloc...
En Java, un bloc est dlimit par des accolades {...} , et tout ce qui est entre accolades constitue un bloc. On peut placer un bloc partout o on peut placer une instruction. Un des principaux
intrts du bloc est de limiter la porte didentification des identificateurs et ainsi amliorer la lisibilit et la fiabilit des programmes. Lexemple suivant illustre lusage dun bloc en Java. Les variables u, v, sPlus et sMoins sont ici locales au bloc introduit.
double resultat;
...
{
double u=a+b; double v=a-b;
double sPlus=sin u; double sMoins=sin v;
resultat=(sPlus+sMoins)/(sPlus-sMoins);
}
...
Ces variables ne servent que dintermdiaires de calcul pour la variable resultat . La prsence
dun bloc est rassurante pour le lecteur : il na pas se demander si u, v, sPlus et sMoins servent autre chose dans le programme, car ces variables sont inconnues en dehors du bloc o elles
sont dclares.
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

61

Modularit

4.5

Modularit
De faon gnrale, la modularit consiste dcouper les gros programmes en morceaux, chaque
morceau concernant un thme.
La modularit intervient plusieurs niveaux. En Java, on peut distinguer trois niveaux :

Les fonctions : une fonction ralise un morceau de traitement bien spcifi (calcul dune fonc-

tion, production de certains effets...)


Les classes : un programme est gnralement compos de plusieurs classes, chacune soccupant
dun thme. Par exemple, la classe Math de la bibliothque standard regroupe toutes les fonctions et constantes mathmatiques usuelles : sinus, cosinus, exponentielle, ...
Les paquetages (package ) : un paquetage regroupe plusieurs classes concernant un mme
domaine. Par exemple, le paquetage io (input-output) de la bibliothque standard regroupe les
classes qui permettent de lire et dcrire des donnes.
La modularit prsente de nombreux avantages :

Le dcoupage en petites units amliore la lisibilit et la maintenance des programmes.


Si ces units concernent un thme bien identifi, avec des fonctionnalits bien spcifies, elles
sont gnralement rutilisables dans de nombreuses applications.

4.5.1

Dcoupage en classes
Les fonctions ou donnes dclares dans une classe sont accessibles depuis les autres classes condition dtre affubles du vocable public .
Si une fonction f ou une donne x (statiques) sont dclares dans une classe C, pour lutiliser
depuis une autre classe il faut utiliser une notation pointe :
C.f() ou C.x
Cest ainsi, par exemple, que lon utilise les fonctions et donnes de la classe Math de la bibliothque standard : Math.sin(x) , Math.PI ...
class Math {
...
public static final double PI =
...
...
public static double sin(double x) {...}
...
}

class Test {
... Math.PI
....
... Math.sin(teta)
}

...

Ou bien encore les fonctions de lectures depuis le clavier dfinies dans la classe Lecture du
paquetage es offert pour les exercices : Lecture.chaine() , Lecture.unEntier() ...

62

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Nommage, portes didentification, dures de vie, modularit

4.5.2

Paquetages
En Java, les classes concernant un mme domaine peuvent tre regroupes en paquetages. Les programmes sources des classes dun paquetage appel xxx commencent par la directive :
package xxx;
Les classes dun paquetage doivent tre places dans un rpertoire de mme nom que le paquetage.
Au sein dun paquetage, on a accs aux classes de ce paquetage et aux classes dclares public
des autres paquetages. Pour dsigner une classe dun autre paquetage, on utilise un nom absolu de
la forme :
nom-du-paquetage.nom-de-classe
Il est possible de dsigner les classes par leur nom court, sans prciser le paquetage, en utilisant la
directive import :
import nom-de-paquetage .nom-de-classe ; pour utiliser une classe du paquetage
import nom-de-paquetage .*; pour pouvoir utiliser toutes les classes du paquetage.
A.java
package xxx ;
public class A {
...
B.java
}
package xxx ;

rpertoire xxx
A.class
B.class

public class B {
...
}

unProgramme.java
import xxx ;
.........
... A ...
... B ...
.........

Les paquetages permettent notamment de structurer les bibliothques dintrt gnral. Par exemple, la bibliothque standard de Java est dcoupe en de nombreux paquetages, par exemple :
java.io : concerne les entres et sorties, notamment pour accder aux fichiers,
java.awt et javax.swing : concerne lusage dinterfaces graphiques (fentres, boutons, zone
de texte...)
java.applet : concerne la programmation dapplets (programmes java tlchargs via le Web
et excuts sur la machine de lutilisateur)
java.net : concerne les accs un rseau,
etc...

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

63

Modularit

Pour pouvoir utiliser les paquetages, le compilateur et lexcuteur de Java doivent connatre les
rpertoires o se trouvent ces paquetages. Cela est indiqu par une variable denvironnement qui
sappelle CLASSPATH (Une variable denvironnement est une variable que lon dfinit, par une
commande, au niveau du systme dexploitation de lordinateur et qui sert paramtrer les fonctions de base du systme et les applications). La variable denvironnement CLASSPATH doit contenir la liste des rpertoires qui contiennent les rpertoires des paquetages, par exemple :
/udd/API/paquetagesMaison:.
Les noms des rpertoires sont spars par :. Dans cet exemple, le rpertoire /udd/API/
paquetagesMaison contient les paquetages maison pour les travaux pratiques de ce cours et
le symbole . signifie le rpertoire courant.
Lendroit o se trouvent les classes utiliser peut galement tre indiqu au moment de la compilation ou de lexcution par une option -classpath :
javac
tion,

-classpath

/udd/API/paquetagesMaison:.

java -classpath /udd/API/paquetagesMaison:. Prog

64

Module A.P.I

Algorithmique Programmation Imprative

pour
Prog.java
la compilapour lexcution.

Universit de Rennes 1

Nommage, portes didentification, dures de vie, modularit

QCM 4.1
A propos du choix des identificateurs, on peut affirmer :

1 - Il faut utiliser les noms les plus courts possibles car cela rduit la taille des programmes et les
rend plus lisibles.
2 - Dans la norme usuelle, les noms de classe commencent par une majuscule et les noms des
donnes et des fonctions commencent par une minuscule.
3 - Il faut utiliser les noms les plus signifiants possibles, mme si cela conduit des noms longs,
car cela remplace avantageusement de nombreux commentaires et rend les programmes plus lisibles.
4 - On a le droit dutiliser villeDeMilleHabitants comme identificateur.
5 - On a le droit dutiliser ville De Mille Habitants comme identificateur.
6 - On a le droit dutiliser villeDe1000habitants comme identificateur.
7 - On a le droit dutiliser 1000habitants comme identificateur.
8 - On a le droit dutiliser classe comme identificateur.
9 - On a le droit dutiliser class comme identificateur.

QCM 4.2
On considre le programme suivant :
class QCMLocalGlobal{
static int x;
static int plusK(int x, int k){
int resul=x+k;
return resul;
}
static void ajouteK(int k){
int resul=x+k;
x=resul;
}

public static void main(String[] z){


x=56;
int k=12;
int troisPlusK = plusK(3,k);
System.out.print("x="+x+" troisPlusK="+troisPlusK);
ajouteK(10);
System.out.println(" x="+x+" troisPlusK="+troisPlusK);
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

65

Modularit

1 - Dans le main, linstruction x=56; donne la valeur 56 au paramtre x de la fonction


plusK .
2 - Dans le main, linstruction x=56; donne la valeur 56 la variable globale x.
3 - Dans linstruction int resul=x+k; de la procdure ajouteK , x dsigne la variable

locale x du main.

4 - Dans linstruction int


globale x.

resul=x+k; de la procdure ajouteK , x dsigne la variable

5 - Lidentificateur resul dsigne la mme variable dans la fonction plusK et dans la procdure ajouteK .
6 - Ce programme affiche :
7 - Ce programme affiche :

x=56 troisPlusK=15 x=66 troisPlusK=15


x=56 troisPlusK=68 x=13 troisPlusK=68

QCM 4.3

1 - Une application est gnralement constitue de plusieurs classes.


2 - Une application doit tre constitue dune seule classes.
3 - Il est bon de regrouper dans une mme classe les choses concernant un mme thme.
4 -la classe Math de la bibliothque standard regroupe les fonctions et les constantes concer-

nant les oprations numriques (mathmatiques).

5 - Il est impossible dutiliser une fonction dune classe A depuis une autre classe B.
6 - Pour utiliser une chose statique appele f dfinie dans dune classe A depuis une autre classe

B, il faut la dsigner par A(f).

7 - Pour utiliser une chose statique appele f dfinie dans dune classe A depuis une autre classe
B, il faut la dsigner par A.f.
QCM 4.4

1 - Un paquetage permet de regrouper des classes concernant un mme domaine dintrt.


2 - Lusage de paquetages napporte rien quand la modularit.
3 - Lusage de paquetages permet un niveau de modularit plus vaste que les classes.
4 - Les paquetages permettent de structurer les bibliothques dintrt gnral.
5 - La variable denvironnement CLASSPATH indique (au compilateur ou lexcuteur de pro-

grammes java) dans quels rpertoires se trouvent les classes et les paquetages utiliser.

6 - Loption -classpath des commandes javac et java indique (au compilateur ou lexcuteur de programmes java) dans quels rpertoires se trouvent les classes et les paquetages utiliser.
7 - Pour utiliser les classes dun paquetage ppp, on place gnralement en dbut du texte source
une directive import ppp.*; .
8 - Pour utiliser les classes dun paquetage ppp, il est ncessaire de placer en dbut du texte
source une directive import ppp.*; .
9 - Pour utiliser une classe A dun paquetage ppp, on peut la dsigner par ppp.A .

66

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Nommage, portes didentification, dures de vie, modularit

Exercice 4.1

Portes dindentifications

objectif : comprendre la porte des identifications


Indiquer ce quaffiche le programme suivant :
class TestPorteesDIdentification {
static String texte = "alfred";
static void imprimeTexte(){
String texte="jules";
System.out.println(texte);
texte="dupont";
System.out.println(texte);
}

public static void main(String[]arg){


System.out.println(texte);
imprimeTexte();
System.out.println(texte);
}

Exercice 4.2

Variables globales, variables locales

objectif : comprendre les effets sur les variables globales


On considre le programme suivant :
class TestLocalGlobal {
static int i = 10; ligne A
static int a = 4, b = 8;
static int diff(int x, int y){
int i; ligne B
i = x-y;
return i;
}
static int somDiff(int x, int y){
i = x+y;
return i + diff(x, y);
}

public static void main(String[]arg){


int res;
res=diff(a,b);System.out.println("i="+i);
res=diff(i,a);System.out.println("i="+i);
res=somDiff(a,b);System.out.println("i="+i);
res = diff(b, b);System.out.println("i="+i);
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

67

Modularit

Quelles sont les variables globales et les variables locales ?


Quelles sont les valeurs affiches ?
Que se passe-t-il si on enlve la ligne B ?
Que se passe-t-il si on enlve la ligne A ?

Exercice 4.3

Usage de plusieurs classes

objectif : savoir rdiger un programme compos de plusieurs classes


On dsire calculer le primtre et laire de cercles et de rectangles. Pour raison de modularit, on
dcide de rdiger plusieurs classes :

une classe Cercle , spcialise dans les calculs concernant les cercles,
une classe Rectangle , spcialise dans les calculs concernant les rectangles.
une classe TestModularite contenant la procdure principale qui teste les deux classes prcdentes pour un cercle de rayon 12 et pour un rectangle de cts 12 et 15.

Raliser et tester cet ensemble de classes.


Remarque : on peut ici rdiger les sources des trois classes dans le mme fichier, par exemple
TestModularite.java . Il est cependant prfrable (cest obligatoire dans le cas gnral) de
les placer dans des fichiers spars : Cercle.java et Rectangle.java .

68

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Instructions conditionnelles

CHAPITRE 5

Instructions conditionnelles

objectif : savoir utiliser les instructions conditionnelles.

5.1

Forme gnrale de linstruction conditionnelle


objectif : connatre la forme usuelle des instructions conditionnelles
Les instructions conditionnelles permettent de choisir les instructions excuter en fonction de certains critres. La forme la plus rpandue permet de choisir dexcuter une action parmi un ensemble
dactions en fonction de critres indiqus par des expressions boolennes. Cela peut se reprsenter
schmatiquement par :
si condition1 excuter instructions1
sinon si condition2 excuter instructions2
sinon si condition3 excuter instructions3
...
sinon excuter instructions

condition1

condition2

instructions1

instructions2

autre

...

instructions

Ici, condition1, condition2... sont des expressions rsultat boolen. Le lot dinstructions correspondant la premire de ces expressions dont le rsultat est vrai est excut. Si toutes les expressions sont fausses, les instructions du dernier sinon sont excutes.
En Java cela scrit :
if (condition1) { instructions1 }
else if ( condition2) { instructions2 }
else if ( condition3) { instructions3 }
...
else { instructions }
En Java, on nexige pas que les conditions soient exclusives : une condition nest teste que si les
prcdentes sont fausses. Le dernier cas, introduit par un simple else, correspond au cas o
aucune condition nest vraie. Il est optionnel : si on ne le met pas, rien nest excut si aucune condition nest vraie. Les instructions de chacune des branches de la construction conditionnelle propose sont encadres daccolades {...} et constituent donc un bloc. Ce nest vraiment obligatoire
que sil y a une squence de plusieurs instructions dans la branche, mais il vaut mieux mettre systmatiquement les accolades, mme pour une seule instruction, de faon ne pas changer de forme si
des modifications de programme obligent rajouter des instructions.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

69

Forme gnrale de linstruction conditionnelle

Exemple 1 : fonction signe


1

signe ( n ) = 0

+1

si n<0
si n=0
si n>0

Cette fonction peut scrire :


static int signe(int n) {
// rsultat
: -1, 0 ou +1 selon que n est <0, =0 ou >0
int resul;
if (n<0) {
resul = -1;
}
else if (n==0) {
resul = 0;
}
else /* n>0 */ { resul = 1;
}
return resul;
}
Toutes les conditions sont ici mutuellement exclusives. La dernire condition, n>0, est ncessairement vraie si les prcdentes sont fausses. Il est recommand dindiquer la condition quon sait tre
vraie en commentaire, /* n>0 */
, cela rend le programme plus facile comprendre et maintenir.
Remarque :
Java ne permettrait pas dcrire la dernire branche de la conditionnelle avec un else if
else if (n>0) {resul=1;}
return resul;
car le compilateur nadmet pas quune variable puisse ne pas tre initialise. Certes, la condition
n>0 est certainement vraie si les prcdentes sont fausses, mais le compilateur ne le sait pas (cette
proprit fait partie de la smantique du programme, et le compilateur ne sait appliquer que des
rgles de syntaxe). Effectivement, si aucune condition ntait vraie, la variable resul ne serait pas
initialise.
On peut se passer de la variable locale resul si on accepte les instructions return depuis le sein
de la conditionnelle :
static int signe(int n) {
// rsultat
: -1, 0 ou +1 selon que n est <0, =0 ou >0
if (n<0) {
return -1;
}
else if (n==0) {
return 0;
}
else /* n>0 */ { return 1;
}
}
Ici encore, Java ne permettrait pas dcrire la dernire branche de la conditionnelle
else if (n>0) {

return 1;

car le compilateur nadmet pas quune fonction qui doit rendre un rsultat puisse se terminer sans
instruction return .

70

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Instructions conditionnelles

Exemple 2 : fonction max


a
max (a,b) =
b

si a b
si a < b

En Java cela donne :


static int max(int a, int b) {
// rsultat
: le maximum de a et b
if (a>=b) {
return a;
}
else /* a<b */ { return b;
}
}

Exemple 3 :
La fonction decodeMois rend en rsultat le nom du mois de lanne dont le numro est pass en
paramtre. Le numro du mois doit tre un entier compris entre 1 et 12. Le nom du mois est une
chane de caractres de type String .
static String decodeMois(int numeroDeMois) {
// prrequis
: numeroDeMois>=1 et numeroDeMois<=12
// rsultat
: le nom en clair du mois de numro numeroDeMois
if (numeroDeMois==1) {
return "janvier";}
else
if (numeroDeMois==2) {
return "fvrier";}
else
if (numeroDeMois==3) {
return "mars";}
else
if (numeroDeMois==4) {
return "avril";}
else
if (numeroDeMois==5) {
return "mai";}
else
if (numeroDeMois==6) {
return "juin";}
else
if (numeroDeMois==7) {
return "juillet";}
else
if (numeroDeMois==8) {
return "aot";}
else
if (numeroDeMois==9) {
return "septembre";}
else
if (numeroDeMois==10) {
return "octobre";}
else
if (numeroDeMois==11) {
return "novembre";}
else
/* numeroDeMois==12 */{ return "dcembre";}
}

Remarque sur la notion de prrequis :


Dans la spcification de cette fonction, nous avons dcid de mettre en prrequis la ncessit que le
numro pass en paramtre soit un numro de mois acceptable (compris entre 1 et 12). Ceci est clairement indiqu dans le commentaire de spcification :
// prrequis

: numeroDeMois>=1 et numeroDeMois<=12

Un tel prrequis na pas tre vrifipar le programme de la fonction, mais doit tre respect par
celui qui lappelle. En dautres termes : tant pis pour lutilisateur de la fonction sil viole le prrequis, le rsultat a le droit dtre nimporte quoi. Ici le rsultat sera "dcembre" dans ce cas, mais
cest un nimporte quoi comme un autre, et cela ne fait pas partie de la spcification de la fonction.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

71

Imbrication de conditionnelles

Instruction if sans else :


Dans un langage impratif, les branches dune instruction conditionnelle sont des instructions, qui
font des choses. Parfois il faut excuter certaines instructions si une condition est vraie et ne rien
faire si elle est fausse. Pour cela on peut utiliser une conditionnelle sans rubrique else :
if (condition) { instructions }
Cette forme est strictement quivalente :
if (condition) { instructions } else { /* rien */}
Exemple :
Voici un extrait de programme qui imprime la taille dune personne et signale quelle est trs grande
si sa taille dpasse 2 mtres :
...
if (taillePersonne>2) {
System.out.println("elle est trs grande");
}
...
Remarque : cette forme de conditionnelle une seule branche est typique des langages impratifs.
On ne la trouve pas dans les langages fonctionnels, car dans ces langages la conditionnelle est une
expression : elle signifie une valeur, et elle doit valoir quelque chose dans tous les cas.

5.2

Imbrication de conditionnelles
objectif : savoir choisir entre des conditionnelles imbriques ou une conditionnelle tale
La construction conditionnelle est elle-mme une instruction. On peut donc utiliser une conditionnelle au sein dune conditionnelle.
Considrons par exemple la fonction qui tant donn un numro de mois donne le nombre de jours
de ce mois. Pour simplifier, on ne sintresse pas aux annes bissextiles. Le nombre de jours est
donc :

pour les mois de numro infrieur 8 (janvier... juillet), 30 jours pour les mois pairs, sauf 28

jours pour fvrier, et 31 jours pour les mois impairs,


pour les mois de numro suprieur 8 (aot... dcembre), 31 jours pour les mois pairs et 30
jours pour les mois impairs.
La fonction nombreDeJoursDuMois propose ici ralise ce calcul en utilisant des conditionnelles imbriques :

72

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Instructions conditionnelles

static int nombreDeJoursDuMois(int numeroDeMois) {


// rsultat
: nombre de jours du mois de numro numeroDeMois
if (numeroDeMois<8) { // cas janvier... juillet
if (numeroDeMois%2 == 0) {
if (numeroDeMois==2) { // cas spcial fvrier
return 28;
}
else {
return 30;
}
}
else /* numeroDeMois impair */ { return 31;
}
}
else /* numeroDeMois>8 */ { // cas aot... dcembre
if (numeroDeMois%2==0) {
return 31;
}
else /* numeroDeMois impair */ { return 30;
}
}
}
Conditionnelle tale :
On notera que lutilisation de conditionnelles imbriques ne facilite pas la lecture des algorithmes.
Leur remplacement, quand cest possible, par une conditionnelle tale, quitte complexifier les
conditions, est souvent plus lisible. Lexemple prcdent peut se rcrire sous la forme :
static int nombreDeJoursDuMois(int numeroDeMois) {
// rsultat
: nombre de jours du mois de numro numeroDeMois
if (numeroDeMois==2) { // cas particulier fvrier
return 28;
}
else if (numeroDeMois<8 & numeroDeMois%2==0) {
// cas janvier... juillet, mois pair
return 30;
}
else if (numeroDeMois<8 & numeroDeMois%2==1) {
// cas janvier... juillet, mois impair
return 31;
}
else if (numeroDeMois>=8 & numeroDeMois%2==0) {
// cas aot... dcembre, mois pair
return 31;
}
else /* (numeroDeMois>=8 & numeroDeMois%2==1) */ {
//
cas aot... dcembre, mois impair
return 30;
}
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

73

Aiguillage

5.3

Aiguillage
La plupart des langages de programmation offrent une forme particulire de conditionnelle, que
lon peut appeler aiguillage, qui permet dexcuter un bloc dinstructions choisi parmi plusieurs
selon la valeur dune expression de type scalaire discret : entier, caractre, type numr. La
forme gnrale dune telle construction est :
aiguillage selon la valeur de expression
soit valeur1 : excuter instructions1
soit valeur2 : excuter instructions2
soit valeur3 : excuter instructions3
...
autres cas : excuter instructions
Cette construction nest pas trs gnrale car le critre du choix ne peut tre que la valeur dune
expression scalaire et on na droit qu des constantes pour valeur1, valeur2... On peut se passer de
cette construction car elle est strictement quivalente :
v=expression
aiguillage selon la valeur de v
si v=valeur1 excuter instructions1
sinon si v=valeur2 excuter instructions2
sinon si v=valeur3 excuter instructions3
...
sinon excuter instructions
Laiguillage est utilis essentiellement pour des raisons de performance : le compilateur le traduit
en une instruction de la machine qui, selon la valeur de lexpression, saute directement, en un
coup, la branche excuter.
En Java la forme de cette instruction est :
switch ( expression) {
case valeur1: instructions1 break;
case valeur2 : instructions2 break;
case valeur3 : instructions3 break;
...
default
:instructions
}
Cette instruction nest pas trs heureuse cause des break; quil faut placer en fin de chacune
des branches. Si on ne met pas le break; , au lieu de poursuivre lexcution la suite de
laiguillage, la branche suivante est excute. Ceci est un hritage de mauvais got du langage C.
titre dexemple, voici un programme qui reprend les fonctions prcdentes, decodeMois et
nombreDeJoursDuMois en utilisant laiguillage. La procdure principale de ce programme lit
au clavier un numro de mois et affiche lcran le nombre de jours du mois correspondant. On
notera sur cet exemple que lon peut regrouper les cas qui donnent lieu un mme traitement.

74

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Instructions conditionnelles

class TestNombreDeJoursDuMois {
static int nombreDeJoursDuMois(int numeroDeMois) {
// rsultat
: nombre de jours du mois de numro numeroDeMois
int resul;
switch (numeroDeMois) {
// janvier, mars, mai, juillet, aot, octobre, dcembre
case 1
: case 3
: case 5
:
case 7
: case 8
: case 10
: case 12
: resul=31; break;
// avril, juin, septembre, novembre
case 4
: case 6
: case 9
: case 11
: resul=30; break;
// fvrier
case 2
: resul=28; break;
default
: resul=0;
}
return resul;
}
static String decodeMois(int numeroDeMois) {
// rsultat
: le nom en clair du mois de numro numeroDeMois
// "mois inconnu" si numeroDeMois nest pas compris
// entre 1 et 12
String resul;
switch(numeroDeMois) {
case
1 : resul= "janvier"; break;
case
2 : resul= "fvrier"; break;
case
3 : resul= "mars"; break;
case
4 : resul= "avril"; break;
case
5 : resul= "mai"; break;
case
6 : resul= "juin"; break;
case
7 : resul= "juillet"; break;
case
8 : resul= "aot"; break;
case
9 : resul= "septembre"; break;
case 10 : resul= "octobre"; break;
case 11 : resul= "novembre"; break;
case 12 : resul= "dcembre"; break;
default
: resul= "mois inconnu";
}
return resul;
}

public static void main(String[] arg) {


System.out.print("numroDeMois
: ");
int numMois=Lecture.unEntier();
System.out.print("il y a ");
System.out.print(nombreDeJoursDuMois(numMois));
System.out.print(" jours en ");
System.out.println(decodeMois(numMois));
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

75

Aiguillage

QCM 5.1
Considrons les fonctions :
static int choix(boolean cond,int k1,int k2){
// rsultat
: k1 si cond, k2 sinon
if(cond){return k1;}
else {return k2;}
}
static int f(int k1, int k2){
// rsultat
: ???
return choix(k1>k2,k1,k2);
}
static int g(int k1){
// rsultat
: ???
return choix(k1>=0,k1,-k1);
}
static int h(int k1, int k2){
// rsultat
: ???
return choix(k1>k2,k1-k2,k2-k1);
}

1 - Le rsultat de la fonction f(12,56) vaut 44.


2 - Le rsultat de la fonction f(56,12) vaut -44.
3 - Le rsultat de la fonction f(12,56) vaut 56.
4 - Le rsultat de la fonction f est la somme de k1 et k2.
5 - Le rsultat de la fonction f est la diffrence de k1 et k2.
6 - Le rsultat de la fonction f est le maximum de k1 et k2.
7 - Le rsultat de la fonction f est la valeur absolue de la diffrence de k1 et k2.
8 - Le rsultat de la fonction g est k1.
9 - Le rsultat de la fonction g est -1 si k1>=0 , +1 sinon.
10 - Le rsultat de la fonction g est la valeur absolue de k1.
11 - Le rsultat de la fonction g est k1 si k1>=0 , -k1 sinon.
12 - Le rsultat de la fonction h(12,56) vaut 44.
13 - Le rsultat de la fonction h(56,12) vaut -44.
14 - Le rsultat de la fonction h(12,56) vaut 56.
15 - Le rsultat de la fonction h est la somme de k1 et k2.
16 - Le rsultat de la fonction h est la diffrence de k1 et k2.
17 - Le rsultat de la fonction h est le maximum de k1 et k2.
18 - Le rsultat de la fonction h est la valeur absolue de la diffrence de k1 et k2.
76

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Instructions conditionnelles

QCM 5.2
Considrons la fonction :
static
String
choix(int
// prrequis
: ???
// rsultat
: ???
if(c==1){return s1;}
else if (c==2){return s2;}
else /*c==3*/{return s3;}
}

c, String

1 - Il y a des erreurs de syntaxe.


2 - choix(2,"alfred","toto","dupont")
3 - choix(2,"alfred","toto","dupont")
4 - choix(2,"alfred","toto","dupont")
5 - choix(5,"alfred","toto","dupont")
6 - choix(5,"alfred","toto","dupont")
7 - Les commentaires de spcification :
// prrequis
// rsultat

s1,

String

s2,

String

s3){

vaut "alfred" .
provoque une erreur lexcution.
vaut "toto" .
provoque une erreur lexcution.
vaut "dupont" .

: c>=1 et c<=3
: s1, s2 ou s3 selon que c vaut 1, 2 ou 3

sont acceptables pour cette fonction.

8 - Les commentaires de spcification (sans prrequis) :


// rsultat

: s1, s2 ou s3 selon que c vaut 1, 2 ou 3

sont acceptables pour cette fonction.

9 - Les commentaires de spcification (sans prrequis) :


// rsultat
: s1 si c vaut 1, s2 si c vaut 2,
// s3 pour toute autre valeur de c
sont acceptables pour cette fonction.

10 - Les commentaires de spcification :


// prrequis
: c>=1 et c<=3
// effet
: affiche s1, s2 ou s3 selon que c vaut 1, 2 ou 3
sont acceptables pour cette fonction.

11 - La fonction choix pourrait tre programme en utilisant laiguillage suivant :


static
String
choix(int
switch(c){
case 1
: return s1;
case 2
: return s2;
default
: return s3;
}
}

c, String

s1,

String

s2,

String

12 - La fonction choix ne peut pas tre programme par laiguillage prcdent car cest une
erreur de syntaxe.
13 - La fonction choix ne peut pas tre programme par laiguillage prcdent car labsence
dinstructions break dans les branches rend le rsultat incorrect.
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

77

s3){

Aiguillage

QCM 5.3
Considrons la fonction :
static boolean comprisEntre(int k, int a, int b){
// prrequis
: a<=b
// rsultat
: indique si a<=k<=b
boolean resul;
if(k<a){resul=false;}
if(k>b){resul=false;}
else {resul=true;}
return resul;
}

1 - Il y a des erreurs de syntaxe.


2 - Cette fonction est incorrecte (le rsultat ne correspond pas la spcification).
3 - Cette fonction est correcte.
4 - comprisEntre(2,14,56) vaut true.
5 - comprisEntre(2,14,56) vaut false .
6 - comprisEntre(62,14,56) vaut true.
7 - comprisEntre(62,14,56) vaut false .
QCM 5.4
Considrons la fonction :
static int valeurDeCarte(String c){
// prrequis
: c vaut "valet", "dame", "roi" ou "as"
// rsultat
: valeur de la carte de nom c
// (respectivement 1, 2, 3 et 10)
switch(c){
case "valet"
: return 1;
case "dame"
: return 2;
case "roi"
: return 3;
default
: return 10;
}
}

1 - Il y a des erreurs de syntaxe.


2 - Cette fonction peut provoquer une erreur lors de lexcution.
3 - Cette fonction peut rendre un rsultat incorrect (rsultat qui ne correspond pas la spcifica-

tion).

4 - Cette fonction est correcte.

78

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Instructions conditionnelles

Exercice 5.1

Maximum de trois nombres

objectif : savoir utiliser la conditionnelle.


Rdiger et tester les fonctions suivantes :
1 - Fonction max qui calcule le plus grand de deux entiers donns en paramtres.
2 - Fonction maximum de trois nombres qui calcule le plus grand de trois entiers donns en paramtres. En raliser deux versions :

version1 : maxDeTroisNombresV1 , en utilisant la fonction max,


version2 : maxDeTroisNombresV2 , directement, sans utiliser la fonction max.

Exercice 5.2

Mdiane

Rdiger une fonction qui calcule la mdiane de trois entiers distincts, cest--dire celui qui est
encadr par les deux autres.
Exemple : la mdiane de 8, 1, 4 est 4.

Exercice 5.3

Equation du second degr

objectif : savoir analyser tous les cas pertinents sur les donnes et matriser limbrication de
conditionnelles.
Rdiger un programme qui affiche la ou les solutions dune quation du second degr :
ax2+bx+c=0
Attention : faire une analyse srieuse. Les nombres a,b,c sont des nombres quelconques. Ils peuvent notamment tre nuls.
Conseil : rdiger une fonction qui rende en rsultat une chane de caractres signifiant en clair le
nombre et la nature des solutions.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

79

Aiguillage

Aide 5.1

Maximum de trois nombres

Pour maxDeTroisNombresV1 :
utiliser le fait que maximum(a,b,c) = max(a,max(b,c))
Pour maxDeTroisNombresV2 :
on peut utiliser une conditionnelle tale trois branches dont les branches correspondent aux cas
respectifs o le maximum est a, b ou c.
Aide 5.2

Mdiane

Analyser le problme selon les trois rsultats possibles (a, b ou c), ce qui conduit une conditionnelle tale avec les trois conditions qui expriment respectivement que a, b ou c est la mdiane.
Aide 5.3

Equation du second degr

Nous avons choisi de raliser une fonction solution qui reoit en paramtres les trois coefficients a,
b et c et rend en rsultat une chane de caractre qui signifie en clair le nombre et la nature des
solutions : static String solution(double a, double b, double c)
class TestEquationSecondDegre {
static String solution(double a, double b, double c) {
// rsultat
: les solutions en clair de lquation ax2+bx+c=0
...
}

public static void main(String[] arg) {


System.out.println("4X2 - 8X + 4
...
}

: "+solution(4,-8,4));

Lanalyse des divers cas est la suivante :


Pour a=0 :

si b0, la solution est -c/b


si b=0 :

si c=0, tout nombre est solution (quelque soit x, 0=0)


si c0, il ny a aucune solution (quelque soit x, c0)

pour c0 :

la forme du rsultat dpend du dterminant : delta = b2-4ac


si delta>0, il y a deux solutions qui sont des nombres rels :
b + delta
------------------------------2a

et

b delta
------------------------------2a

si delta=0, il y a une solution double qui est un nombre rel : -b/2a


si delta>0, il y a deux solutions qui sont des nombres complexes :
b + i delta
-----------------------------------2a

80

Module A.P.I

et

b + i delta
-----------------------------------2a

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

CHAPITRE 6

6.1

Rcursivit et itration

Exprimer la rptition
objectif : comprendre deux faons diffrentes de rpter des traitements :
la rcursivit : exprime le rsultat cherch comme solution dquations,
l itration : exprime le rsultat cherch comme lment dune suite de valeurs.

La programmation atteint sa puissance en rptant un bloc dinstructions un nombre de fois non


connu lavance. La rptition est en effet le seul moyen dexprimer de faon finie des quantits de
calculs qui dpendent des donnes traites.
Il y a deux faons dexprimer la rptition : la rcursivit et litration. Ces deux moyens sont trs
diffrents, sauf dans quelques cas particuliers. Il est impossible de dire si lun est mieux adapt que
lautre, ou si lun est plus facile que lautre. Cela dpend du problme rsoudre et galement du
got et des habitudes de celui qui programme. Pour bien saisir la diffrence entre les deux techniques, nous prendrons un exemple trs simple : le calcul de la somme des nombres entiers de 0 n :
somme(n) = 0 + 1 + 2 +...+n
Nous faisons ici semblant dignorer que le rsultat peut sobtenir par la formule n(n+1)/2.

6.1.1

Mthode rcursive
La mthode rcursive est base sur un systme dquations. On cherche des quations qui expriment la fonction calculer pour diverses formes de largument en terme de la mme fonction applique des arguments plus simples. Pour notre exemple, lencadr suivant suggre dexprimer
somme(n) au moyen de somme(n-1) :
somme(n) = 0 + 1 + 2 +... + n
somme(n-1)
Cette formulation nest valable que pour n>0. 0n se pose le problme pour n=0, et bien videmment
la rponse est 0. Ces cas particuliers jouent un rle important : on les appelle les cas de base. Ce
sont les cas pour lesquels le calcul de la solution ne fait pas intervenir la fonction elle-mme. Il est
impratif davoir de tels cas, sinon les quations ne conduisent pas un algorithme. Sans les cas de
base, les quations tournent en rond et ne permettent pas de calculer la fonction en un nombre fini

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

81

Exprimer la rptition

dtapes. Ici, nous avons donc les deux quations :

somme(n) = n+ somme(n-1) pour n>0


somme(0) = 0
Ces quations conduisent la dfinition rcursive suivante de la fonction somme :
static int somme(int n) {
// prrequis
: n>=0
// rsultat
: la somme des entiers de 0 n
if (n>0) {return n+somme(n-1);}
else /*n==0*/ {return 0;}
}
Remarque : les langages de programmation fonctionnels nont que ce moyen de calcul rptitif.

6.1.2

Mthode itrative
La mthode itrative est base sur une suite rcurrente. On cherche une suite dfinie par rcurrence
(cest--dire dont le terme de rang k sobtient par une formule utilisant le terme de rang k-1).
Pour la fonction somme prise en exemple, on envisage la suite :
S0 = 0, S1= 0 + 1, S2= 0 + 1+ 2,...

Sn=0 + 1+ 2...+ n

qui, de toute vidence, peut tre dfinie par la rcurrence :

S0 = 0,
Si = Si-1 + i pour i>0
On a alors : somme(n) = Sn
Le passage de la suite au programme se fait ainsi :

on utilise une variable, s ici, destine valoir successivement les valeurs de la suite,
on initialise cette variable la premire valeur de la suite, 0 ici,
on rpte, en Java au moyen dune instruction while , une instruction daffectation de cette
variable qui fait passer sa valeur du terme de rang i-1 au terme de rang i, et ce jusqu obtention
du terme qui nous satisfait, le terme de rang n ici.

Pour savoir si on a obtenu le terme de rang n, il faut dans cet exemple une variable auxiliaire, i, qui
vaut en permanence le rang du terme calcul dans s. La variable i est initialise 0 et incrmente
chaque rptition de litration.
Cela conduit la dfinition itrative suivante de la fonction somme :
static int somme(int n) {
// prrequis
: n>=0
// rsultat
: la somme des entiers de 0 n
int s=0; int i=0;
while(i<n) {s=s+i; i=i+1;}
return s;
}

82

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

6.2

Rcursivit
objectif : savoir rdiger des fonctions simples de manire rcursive.

6.2.1

Exemples de dfinitions rcursives de fonctions


Exemple 1 : fonction factorielle
La fonction factorielle sapplique un entier strictement positif n et donne en rsultat le nombre
entier produit des entiers de 1 n :
factorielle(n) = 12...n

(on la note gnralement n!)

La factorielle satisfait aux quations :


1
si n = 1

factorielle ( n ) =
si n > 1
n factorielle ( n 1 )
Ceci conduit la programmation rcursive suivante :
static int factorielle(int n) {
// prrequis
: n>0
// rsultat
: la factorielle de n
if (n==1) {return 1;}
else {return n*factorielle(n-1);}
}
Exemple 2 : fonction pgcd
La fonction pgcd rend comme rsultat le plus grand commun diviseur de deux nombres entiers
strictement positifs a et b. Le calcul rcursif peut-tre tabli partir des quations bien connues
suivantes :

a
si a=b

pgcd (a,b) = pgcd (a b,b)


si a>b

si a<b
pgcd (a,b a)
Ce qui conduit la dfinition rcursive :
static int pgcd(int a, int b) {
// prrequis
: a>0 et b>0
// rsultat
: le pgcd de a et b
if (a==b) {return a;}
else if (a>b) {return pgcd(a-b,b);}
else /* a<b */ {return pgcd(a,b-a);}
}
Ces exemples montrent que le passage des quations au programme rcursif est systmatique. Chaque quation donne lieu une branche de conditionnelle :

dont la condition teste lappartenance des paramtres au domaine de validit de lquation,


dont le corps est une instruction return ayant pour expression le second membre de lquation.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

83

Terminaison dun algorithme rcursif

6.3

Terminaison dun algorithme rcursif


objectif : savoir justifier quun algorithme rcursif termine en un temps fini.
Un calcul rcursif est la traduction directe dquations que lon sait mathmatiquement correctes.
Il sensuit que si la fonction calcule un rsultat, il est ncessairement correct. Encore faut-il que le
rsultat soit effectivement calcul. Pour cela il faut sassurer que le processus dvaluation se termine, cest--dire que la fonction ne va pas se rappeler indfiniment.
Dans la fonction factorielle , les paramtres des appels successifs sont n, n-1, n-2... jusqu 1
pour lequel il ny a pas dappel rcursif, le rsultat de la fonction valant alors 1.
La terminaison est le principal point, voire pratiquement le seul, sassurer pour garantir lexactitude dun calcul rcursif. Lautre facteur, la validit des quations, est un problme de mathmatique, pas vraiment dinformatique. Cest dailleurs cette facilit de programmation et cette scurit
quant la validit des rsultats qui font lattrait de la programmation rcursive. La garantie de terminaison nest cependant pas toujours un problme trivial. Ainsi le systme suivant dquations
pour la factorielle nest pas faux, mais il ne conduit pas un algorithme rcursif qui termine :

1
si n = 1

factorielle ( n ) = factorielle ( n + 1 )
si n > 1
---------------------------------------------n+1

Pour sassurer de la terminaison, il faut considrer la ou les suites possibles dappels rcursifs et
vrifier que cette suite atteint en un nombre fini de termes les cas pour lesquels il ny a pas dappel
rcursif, appels cas de base.
Un moyen systmatique et formel de sen assurer est de trouver une fonction de terminaison. Cest
une fonction (mathmatique) qui a les proprits suivantes :

elle dpend des arguments de la fonction dfinie rcursivement,


son rsultat est un entier positif ou nul,
son application aux arguments des appels rcursifs a une valeur strictement infrieure son
application aux arguments de la fonction dfinie rcursivement.

Dans le cas de factorielle , on peut considrer la fonction de terminaison :


fonctionDeTerminaison(n) = n
Elle satisfait bien aux critres ci-dessus :

elle est positive (par prrequis sur largument de factorielle, n est positif),
largument des appels rcursifs, n-1 ici, est strictement infrieur n.
Pour la fonction pgcd, trouver une fonction de terminaison est moins trivial. On peut prendre :
fonctionDeTerminaison(a,b) = a+b

Cette fonction est bien positive, par prrequis sur les arguments a et b.
dans le cas a>b : lappel rcursif se fait avec a-b et b et a-b+b = a est bien strictement infrieur

a+b puisque b est >0.


dans le cas a<b : lappel rcursif se fait avec a et b-a et a+b-a = b est bien strictement infrieur
a+b puisque a est >0.
Lalgorithme termine donc.

84

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

La notion de fonction de terminaison sert prouver la terminaison dun calcul rcursif. La conception dun algorithme est souvent une activit intuitive, base sur des analogies et des expriences
acquises et concevoir un algorithme nest pas la mme chose que de le prouver. On fait rarement
une preuve formelle des algorithmes, mais il est tout de mme essentiel de savoir que cela existe,
que tout programme correct ne marche pas par hasard. Il est satisfaisant pour lesprit de savoir que,
si on veut, on peut faire de telles preuves.

6.4

Gnralits sur lItration


objectif : savoir rdiger des fonctions simples de manire itrative.

6.4.1

Forme gnrale dune itration


Une itration calcule un rsultat sous la forme dun terme dune suite rcurrente u0, u1, u2..., qui
satisfait une certaine condition cond. Les termes successifs de la suite nont pas tre conservs.
Il suffit dutiliser une seule variable pour conserver la valeur de llment courant de la suite.
chaque tape, llment suivant est calcul et affect la variable.

u=a

Le schma de calcul itratif est donc :

cond(u)

u=a
tant que cond(u) est fausse faire u = f(u)

vrai

faux

instructions
qui ralisent
u = f(u)

En Java, la forme gnrale de litration appele boucle tant-que1 est :


while ( non condition darrt ) { instructions }
Lexcution de cette boucle consiste excuter les instructions entre accolades, appeles corps de la
boucle, tant que la condition est vraie.
Lexemple prcdent de calcul dune suite rcurrente ui avec une formule de rcurrence exprimable
par une fonction f scrit donc :
u = a;
while (!cond(u)) {u=f(u);}

1. while loop en anglais.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

85

Gnralits sur lItration

6.4.2

Premier exemple de calcul itratif : racine carre


Pour calculer la racine carre dun nombre rel a on peut utiliser la relation de rcurrence suivante
(formule dHron dAlexandrie) :
uo = 1
ui = (ui-1 + a / ui-1) / 2 pour i > 0
La valeur cherche est la limite de ui pour i tendant vers linfini. En informatique, on naime pas
beaucoup linfini. On se contentera dun rsultat ayant une prcision suffisante, disons 10-3 prs.
On cherche donc le premier terme de la suite qui satisfait la condition :
| ui2 - a | < 0.001
On a maintenant tous les lments pour construire le programme itratif de la fonction
racineCarree :

une variable u, de type double , sera utilise, initialise 1, le premier terme de la suite,
la condition darrt de litration sera | u2 - a | < 0.001,
le corps de litration ralisera (u + a/u)/2..
Ce qui donne :
static double racineCarree(double a) {
// prrequis
: a>=0
// rsultat
: racine carre de a 0.001 prs
double u = a;
while (!(Math.abs(u*u-a)<0.001)) {u = (u + a/u)/2);}
return u;
}

6.4.3

Itration sur une composition de plusieurs variables


objectif : savoir raisonner sur la suite de valeurs de plusieurs variables.
Dans lexemple prcdent, litration calcule une suite de valeurs simples, ui, chaque ui tant un
simple nombre rel. Dans le cas gnral, la suite rcurrente concerne une composition de valeurs :
<u, v, w...>i que lon peut galement noter <ui, vi, wi...>
Les lments de la suite et donc leurs composants sont fonction de llment prcdent :
<ui, vi, wi...> = F(<ui-1, vi-1, wi-1...>)
soit encore :
ui = f(ui-1, vi-1, wi-1...)
vi = g(ui-1, vi-1, wi-1...)
wi = h(ui-1, vi-1, wi-1...)
...
Litration qui calcule les lments de la suite est construite comme prcdemment. La seule petite
complication provient de la multiplicit des composants des lments de la suite. Il faut une variable par composant. Ces variables sont appeles variables dtat de la boucle. La forme de litration doit tre :
u = a, v = b, w = c
tant que cond(u,v,w) est fausse faire <u, v, w> = <f(u, v, w),g(u, v, w), h(u, v, w)>

86

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

6.4.3.1 Exemple : fonction factorielle


A partir de factorielle(1)=1 et, pour i>1, factorielle(i) = ifactorielle(i-1), nous constatons que lon
peut calculer factorielle(n) au moyen de la suite :
fac1 = 1, fac2 = 21 = 2fac1, fac3 = 321 = 3fac2,... faci = ifaci-1
Mais cette suite de valeurs nest pas rcurrente par elle mme. Il manque le i. Le i doit lui aussi
tre dfini par rcurrence. Nous sommes amens introduire une composante supplmentaire, que
nous appellerons k, dont la suite des valeurs sera les i. Voici la suite des valeurs de k, ainsi que
celles de fac rcrites en faisant intervenir k :
i:

...

ki :

...

ki-1+1

21

32

46

524 ...

faci : 1

(ki-1+1)faci-1

Le rsultat, factorielle(n), est la valeur de faci pour ki= n.


Cette analyse conduit au programme suivant :
static int factorielle(int n){
// prrequis
: n>0 et n pas trop grand
// rsultat
: factorielle de n
int k=1; int fac=1;
while(k<n){
fac=(k+1)*fac;
k=k+1;
}
return fac;
}
Important : dvelopper des scnarios
Cet exemple montre lintrt de dvelopper un ou plusieurs scnarios, cest--dire des exemples de
de valeurs des suites rcurrentes envisages. Cest ce que nous avons fait pour les suites k et fac.
Cest une bonne mthode pour trouver les bonnes formules de rcurrence. On peut facilement se
tromper sinon.
6.4.3.2 Autre exemple de calculs itratifs : pgcd
Comme exemple ditration portant sur une composition de valeurs, considrons le calcul du plus
grand commun diviseur. partir des quations :

a
si a=b

pgcd (a,b) = pgcd (a b,b)


si a>b

si a<b
pgcd (a,b a)
on imagine assez facilement une suite de couples <ui,vi> dont les composants convergent vers
pgcd(a,b). Il sagit de la suite initialise <a,b> et dont llment suivant sobtient en retranchant le
plus petit composant du plus grand. Par exemple, pour a=21 et b=28, la suite <ui,vi> est :
<21,28>, <21,7>,<14,7>,<7,7>
Le pgcd est 7. Llment qui nous intresse dans cette suite se reconnat au fait que ui=vi. La condi-

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

87

Gnralits sur lItration

tion darrt est donc u=v.


Litration correspondante a la forme suivante :
u = a, v = b
tant que uv faire <u, v> = <u-v, v> si u>v , <v, v-u> sinon
ou encore, puisquon constate ici quun seul composant u ou v est modifi chaque tape :
u = a, v = b
tant que uv faire si u>v : u = u-v
sinon : v = v-u
On sest ramen cette seconde forme, car les langages de programmation usuels nont pas, cest
dommage, laffectation multiple telle que u,v = expression. On est oblig de le dcomposer en
u = expression1 et v = expression2.
La fonction pgcd scrit alors :
static int pgcd(int a, int b) {
// prrequis
: a>0 et b>0
// rsultat
: le pgcd de a et b
int u=a; int v=b;
while (u!=v) {
if (u>v) {u=u-v;}
else /*u<v*/ {v=v-u;}
}
return u;
}

6.4.4

Trace dexcution dune itration


Pour illustrer ce qui se passe lors de lexcution dune itration, nous allons indiquer la squence
dinstructions excutes au cours du temps, accompagne des valeurs successives des variables,
dans le cas du calcul de pgcd(21,28). Le programme complet est :
class TestPgcd {
static int pgcd(int a, int b) {
int u=a; int v=b;
(p1)
while (u!=v) {
(p2), test
if (u>v) {
(p3), test
u=u-v; (p4)
}
else /*u<v*/ {
v=v-u; (p5)
}
}
return u;
(p6)
}

88

public static void main(String[] arg) {


System.out.print("pgcd(28,21) = "); (m1)
System.out.println(pgcd(28,21)); (m2)
}

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

trace dexcution

main

pgcd

<u,v>

(m1)
(m2)dbut

(p1)

<28,21>

(p2) uv, boucle


(p3) u>v
(p4)
pgcd(28,21) = 7

<7,21>

(p2) uv, boucle


temps

(p3) u<v
(p5)

<7,14>

(p2) uv, boucle


(p3) u<v
(p5)

<7,7>

(p2) u=v, termine


(m2)fin

6.4.5

(p6)

Petits ennuis dus labsence daffectation multiple


objectif : savoir contourner les problmes dus la squentialit des affectations de plusieurs variables.
Les affectations sont squentielles et il faut en tenir compte pour affecter correctement plusieurs
variables qui reprsentent les composants des lments de la suite calcule par la boucle.
Pour raliser : <u,v>i = F(<u,v>i-1), cest dire ui = f(ui-1,vi-1) et vi = g(ui-1,vi-1)
on est amen effectuer une affectation que lon peut noter : u, v reoivent f(u,v), g(u,v)
mais laffectation multiple ntant gnralement pas offerte par les langages, on est tent de la remplacer par plusieurs affectations simples, ce qui est gnralement incorrect si on utilise les mmes
expressions :
...
u = f(u,v);
incorrect, ne ralise pas <u, v>reoivent f(u,v), g(u,v)
v = g(u,v);
...
La squence ci-dessus ralise en fait : u, v = f(u,v), g(f(u,v),v) puisque la premire affectation u
est dj faite au moment o on affecte v.
Un moyen systmatique et clair de contourner ce petit problme est dutiliser, quand cest vraiment
ncessaire, des variables intermdiaires pour dsigner de faon temporaire le futur tat des compo-

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

89

Notions dinvariant de boucle et de fonction de terminaison

sants. Par exemple, si u et v sont des variables de type double :


...
double uFutur = f(u,v);
ralise <u, v>reoivent f(u,v), g(u,v)
double vFutur = g(u,v);
u=uFutur; v=vFutur;
...
Comme exemple, on peut reprendre le calcul du pgcd, partir des quations :
a
si a=b

pgcd (a,b) =
pgcd(max(a, b) min(a, b), min(a, b))

si a b

Ces quations sont strictement quivalentes aux prcdentes. Cest une autre faon de dire, au
moyen des fonctions min et max, que lon peut remplacer le plus grand des deux nombres par la
diffrence du plus grand et du plus petit. Litration correspondante est :
u = a, v = b
tant que uv faire <u, v> = <max(u,v)-min(u,v), min(u,v)>
Affectation multiple qui, selon le principe prcdent, peut se traduire en :
static int pgcd(int a, int b) {
int u=a; int v=b;
while (u!=v) {
int uFutur=max(u,v)-min(u,v); int vFutur=min(u,v);
u=uFutur; v=vFutur;
}
return u;
}
ou encore, en introduisant une variable minuv pour viter de calculer deux fois min(u,v) :
static int pgcd(int a, int b) {
int u=a; int v=b;
while (u!=v) {
int minuv=min(u,v); int uFutur=max(u,v)-minuv;
u=uFutur; v=minuv;
}
return u;
}

6.5

Notions dinvariant de boucle et de fonction de terminaison


objectif : prouver quune itration termine en un temps fini et calcule bien ce qui est prvu.

6.5.1

Construction dune itration


Deux problmes se posent au concepteur dun algorithme itratif :

dune part inventer litration,


dautre part sassurer de lexactitude de litration, cest--dire tre sr quelle calcule le bon
rsultat dans tous les cas.

Pour linvention, il ny a pas de voie royale. Cest souvent lexprience acquise et lanalogie avec

90

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

des problmes que lon a dj rsolus qui conduisent inventer litration. La mthode consiste
chercher une suite rcurrente qui conduise la solution, et ceci se fait par essai-erreur, en exhibant
des exemples de telles suites.
Pour lexactitude, il existe deux notions importantes qui sont linvariant de boucle et la fonction de
terminaison. Ces notions seront vues aux paragraphes suivants.
Nous allons illustrer la construction dune itration sur un exemple simple. Soit raliser une fonction nbChiffres gale au nombre de chiffres significatifs de lcriture dcimale dun entier strictement positif.
Par exemple : nbChiffres(1998) = 4
Comment peut sobtenir ce rsultat 4 ?
On peut considrer que un chiffre vient du 8 et trois chiffres viennent de 199. Une piste :
nbChiffres(1998) = nbChiffres(199) + 1 = 3+1 = 4
On peut gnraliser ceci en :
pour n < 10, nbChiffres(n) = 1
pour n 10, nbChiffres(n) = NbChiffres(n/10)+1
Remarque :
On aurait pu partir sur une mauvaise ide en considrant, ce qui nest pas faux, que le rsultat 4
sobtient en prenant 1 chiffre de poids fort, le 1, et 3 chiffres de poids faibles 998. On saperoit
que cest une mauvaise ide car on narrive pas exprimer simplement comment extraire ce 1
et ce 998 de 1998 avec les oprations dont on dispose.
ce stade nous avons des quations dont on pourrait tirer un algorithme rcursif vident. Mais
pour un algorithme itratif, il faut trouver une suite rcurrente qui conduise au rsultat.
Les quations nous suggrent de dcomposer le nombre en son chiffre de poids faible (8 dans
lexemple) et le reste constitu des chiffres voir (199 ici). Chaque chiffre extrait doit tre comptabilis pour fournir le rsultat cherch. Ces rflexions nous conduisent considrer la suite de paires
de nombres
<resteAVoiri, compteuri>
o resteAVoiri est le nombre form des chiffres non encore compts et compteuri est le nombre de
chiffres dj compts. Voici le scnario pour n=1998 :
resteAVoiri : 1998 199
compteuri : 0
1

19
2

1
3

0
4

compteuri vaut le rsultat cherch quand resteAVoiri vaut 0


Il ne faut pas hsiter dvelopper un ou plusieurs exemples de suites comme ci-dessus. Ceci permet
de sassurer de la validit de lide et surtout de fixer les valeurs initiales de la suite, les formules de
rcurrence et la condition darrt. Ici on a :

valeurs initiales : resteAVoir0 = n, compteur0 = 0


formules de rcurrence : resteAVoiri+1= resteAVoiri/10, compteuri+1 = compteuri + 1
condition darrt : resteAVoiri = 0

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

91

Notions dinvariant de boucle et de fonction de terminaison

Lalgorithme itratif sen dduit aussitt :

Il comporte deux variables, resteAVoir et compteur , initialises respectivement n et 0,


les formules de rcurrence donnent les affectations du corps de la boucle,
la condition darrt est resteAVoir==0 .
static int nbChiffres(int n) {
// prrequis
: n>0
// rsultat
: nombre de chiffres de lcriture dcimale de n
int compteur=0; int resteAVoir=n;
while (resteAVoir>0) {
compteur=compteur+1; resteAVoir=resteAVoir/10;
}
return compteur;
}

6.5.2

Invariant de boucle
Maintenant que nous avons dcouvert un algorithme itratif, comment sassurer formellement quil
est correct ? la mthode consiste trouver un invariant de boucle. Un invariant de boucle est une
proprit (un prdicat) fonction des variables de la boucle et qui doit :

tre vrai avant la boucle, aprs les initialisations,


tre maintenu vrai par les instructions du corps de boucle (la condition darrt ntant pas
vrifie),
et enfin, pour tre intressant, il faut que linvariant de boucle accompagn de la condition
darrt assurent logiquement que le rsultat est correct.
Pour litration de la fonction nbChiffres , linvariant de boucle est :
compteur

+
nbChiffres(resteAVoir) = nbChiffres(n)

Cette proprit est vraie aprs les initialisations puisque aprs compteur=0 et resteAVoir=n on a bien videmment 0 + nbChiffres(n) = nbChiffres(n)

elle reste vraie chaque excution du corps de boucle puisque, pour resteAVoir >0 les

mathmatiques nous assurent que (proprit bien connue...) :


nbChiffres(resteAVoir) = nbChiffres(resteAVoir/10)+1
et donc si on tient compte de leffet de laffectation compteur=compteur+1 , le bilan est de
conserver la valeur de la somme compteur
+
nbChiffres(resteAVoir)

Lorsque la condition darrt devient vraie, le rsultat est atteint. En effet :


si resteAVoir =0, nbChiffres(resteAvoir) =0, et donc compteur =nbChiffres(n)

92

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

Analogie du citron press


Toute itration ressemble la fabrication dun jus de citron.
Les donnes de lalgorithme sont le citron entier, le rsultat attendu est le jus du citron. Pour faire
un citron press :
Il faut un citron et un rcipient destin recueillir le jus : ce sont les variables de la boucle.
Il faut aussi un presse citron qui chaque tour de main extrait un peu du jus du citron : cest le corps
de la boucle.
On sarrte de presser le citron quand il est devenu sec : cest la condition darrt.
Dans cette analogie linvariant est la conservation du jus : tout moment la somme du jus dans le
rcipient et du jus qui reste dans le citron est gale au jus initialement contenu dans le citron.
Dans la fonction nbChiffres , la variable resteAVoir correspond au citron et la variable
compteur correspond au rcipient qui recueille le jus. La condition darrt citron sec est traduite par resteAVoir ==0. Le pressage du citron correspond aux affectations du corps de boucle.

Les affectations du corps de boucle :


Pour pouvoir justifier que le corps de boucle respecte linvariant, il faut savoir comment tenir
compte des affectations de variables. Dans les cas simples cela peut tre considr comme intuitif et
vident. Cependant voici comment sy prendre plus formellement :
pour une affectation x = expression;
il suffit de montrer que linvariant reste vrai si on substitue dans son texte les occurrences de x par
expression, avec comme hypothses :

que linvariant est vrai,


que la condition de sortie est fausse,
que les conditions des branches de conditionnelles pour lesquelles ces affectations ont lieu sont
vraies.

Dans lexemple prcdent, les affectations sont :


compteur=compteur+1; resteAVoir=resteAVoir/10;
Il faut donc substituer dans linvariant :
compteur par compteur+1 et resteAVoir par resteAVoir/10
compteur+1

nbChiffres(resteAVoir/10)
+
= nbChiffres(n)

est-ce vrai ? oui puisque les mathmatiques nous disent que, sous rserve que resteAVoir >0,
nbChiffres(resteAVoir/10) = nbChiffres(resteAVoir)-1
et donc linvariant substitu redevient :
compteur

+
nbChiffres(resteAVoir) = nbChiffres(n)

ce qui est vrai par hypothse que linvariant est vrai.


Ici nous avons du substituer deux variables (compteur et resteAVoir ). Ceci nest directement
possible que si les affectations sont indpendantes (la nouvelle valeur de compteur ne dpend que
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

93

Notions dinvariant de boucle et de fonction de terminaison

de compteur , et celle de resteAVoir ne dpend que de resteAVoir ). Dans des cas daffectations interdpendantes, il faut tenir compte de la squentialit de ces affectations et corriger les
expressions substitues en consquence.
Ainsi, grce linvariant mis en vidence, on vient de prouver que si la boucle termine, alors le
rsultat est correct. Mais cela ne suffit pas. Encore faut-il sassurer que la boucle termine.

6.5.3

Fonction de terminaison et notion de complexit


Pour sassurer quune itration termine, il faut exhiber une fonction de terminaison. Cest une
notion voisine de celle rencontre pour la programmation rcursive :

cest une fonction des variables de la boucle,


son rsultat est un entier positif ou nul,
elle dcrot strictement chaque excution du corps de la boucle.
Dans lexemple de nbChiffres , on peut choisir pour fonction de terminaison :
fonctionDeTerminaison = resteAVoir
en effet resteAVoir est un entier, toujours >=0, et il dcrot strictement puisque divis par 10
chaque excution du corps de boucle. Attention la subtilit : resteAVoir dcrot strictement
car la condition de litration, resteAVoir>0 , nous assure quil nest pas nul (sinon la dcroissance ne serait pas stricte, car 0/10 nest pas strictement infrieur 0). Il faut se mfier des fausses
fonctions de terminaison, qui ne dcroissent pas toujours strictement : de tels programmes sont
susceptibles de boucler indfiniment.

Notion de complexit :
Il est vident que le nombre dexcutions du corps de boucle est toujours infrieur la fonction de
terminaison applique ltat initial. Ceci permet davoir une ide des performances en temps
dexcution dun algorithme itratif. Si on veut approcher au mieux le temps dexcution, il faut
exhiber une fonction de terminaison aussi petite que possible.
Dans le cas de la fonction nbChiffres on peut prendre la fonction suivante :
autreFonctionDeTerminaison = nbChiffres(resteAVoir )
Cest de toute vidence une fonction de terminaison, elle dcrot de 1 chaque itration car il est
bien connu que n/10 a un chiffre dcimal de moins que n (pour n>0).
Cette fonction de terminaison est bien meilleure que la prcdente. Elle montre que le nombre
dexcution du corps de boucle sera infrieur ou gal nbChiffres(n), cest--dire log10(n)+1.
On dit que la complexit (temporelle) de lalgorithme est O(log n).
Plus gnralement, on dit que la complexit est O(f(n)) si le temps dexcution est infrieur k.f(n),
o k est une constante arbitraire qui abstrait notamment les facteurs technologiques.

94

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

6.5.4

Autre exemple dinvariant de boucle et de fonction de terminaison


Nous considrerons ici un exemple un peu plus complexe, le programme itratif de la fonction
pgcd.
static int pgcd(int a, int b) {
int u=a; int v=b;
while (u!=v) {
if (u>v) {u=u-v;}
else /*u<v*/ {v=v-u;}
}
return u;
}

Invariant propos : pgcd(u,v) = pgcd(a,b)


En effet, les initialisations lassurent trivialement, et le corps de boucle le conserve :
pour le cas u>v

:laffectation u=u-v nous conduit substituer u-v u dans linvariant :

pgcd(u-v,v) = pgcd(a,b)
par la proprit (mathmatique) du pgcd : pour u>v, pgcd(u-v,v) = pgcd(u,v)
donc linvariant substitu est quivalent linvariant initial, vrai par hypothse.
Le raisonnement est analogue pour le cas u<v.
Pour fonction de terminaison on peut proposer |u+v|. Cest bien positif ou nul et cela dcrot strictement chaque itration.

6.6

Itrations sur des entres de donnes


objectif : savoir raisonner sur une squence de donnes lues depuis lextrieur.

On utilise parfois les itrations pour effectuer un traitement sur une squence de donnes introduites
depuis lextrieur au moment de lexcution, notamment des donnes frappes au clavier par lutilisateur du programme. Par exemple pour calculer la somme ou la moyenne dune suite de nombres.
Le programme lit un lment de la squence chaque tape de litration. Une convention doit tre
choisie pour dterminer la fin de la squence. On peut choisir dindiquer la fin par une valeur particulire, qui ne peut pas apparatre dans la squence, appele marque de fin. Par exemple pour une
squence de nombres positifs, la marque de fin pourra tre -1.
Le programme suivant calcule la somme dune squence dentiers frappe au clavier :

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

95

Itrations sur des entres de donnes

class SommeSequenceEntree {
static final int marqueFin=-1;

public static void main(String[] arg) {


int somme=0;
int nombreLu=Lecture.unEntier();
while (nombreLu!=marqueFin) {
somme=somme+nombreLu;
nombreLu=Lecture.unEntier();
}
System.out.print("somme="); System.out.println(somme);
}

Litration utilise deux variables dtat : somme , qui sert cumuler les valeurs lues, et nombreLu
qui reoit chaque tape un nouvel lment de la squence.

Suite associe la squence dentre :


Le programme prcdent est suffisamment simple pour avoir t conu directement (avec un peu
dhabitude). Cependant de tels programmes itratifs qui lisent des donnes peuvent tre conus en
utilisant la mthodologie des suites rcurrentes. Il suffit simplement dintroduire la suite des valeurs
de la squence dentre. Cest une suite qui bien videmment nest pas calcule par une formule de
rcurrence, elle est considrer comme donne.
Pour le problme prcdent du calcul de la somme, la squence dentre doit tre formalise par
une suite nombreLu0, nombreLu1, nombreLu2... suite finie dont le dernier lment est -1. La rcurrence qui permet de calculer la somme est :
somme0 =0
sommei =sommei-1 +nombreLui-1 pour i>0 et tel que nombreLuj -1 pour tout j<i
Par exemple, pour la squence dentre : 6 4 8 2 5 -1,
la suite rcurrente <somme, nombreLu>i est :
i:
0
nombreLui : 6
sommei :
0

4
6

8
10

2
18

5
20

-1
25

On retrouve bien la mme traduction en boucle que pour une suite rcurrente entirement dfinie
par calcul, la seule diffrence est que le passage de nombreLui-1 nombreLui se fait au moyen de
linstruction de lecture :
nombreLu = Lecture.unEntier();

96

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

Autre exemple ditration sur entre de donnes :


Soit une squence de nombres entiers positifs ou nuls introduite au clavier et termine par la marque de fin -1. On veut crire un programme qui indique le nombre de zros rencontrs dans cette
squence.
Lanalyse consiste chercher une relation de rcurrence utilisant les lments de la squence donne, appelons-les nombreLui, et une autre suite, appelons-la nbZeros, telle que nbZerosi contienne
le nombre doccurrences de 0 parmi nombreLu0...nombreLui-1.
Exemple :
i:
0
nombreLui : 6
nbZerosi :
0

0
0

8
1

2
1

0
1

7
2

-1
2

La relation de rcurrence est :


nbZeros i 1 + 1
nZeros i =
nbZeros i 1

si nombreLu i 1 = 0
si nombreLu i 1 0

Les variables de la boucle sont donc nbZeros et nombreLu . Linitialisation de nbZeros est 0 et
celle de nombreLu est faite par une premire lecture avant la boucle, comme cest souvent le cas
pour une boucle qui traite une squence de donnes lues. La condition darrt est bien videmment
nombreLu==marqueFin . Le programme peut scrire :
class NombreDeZeros {
static final int marqueFin=-1;

6.7

public static void main(String[] arg) {


int nbZeros=0;
int nombreLu=Lecture.unEntier();
while (nombreLu!=marqueFin) {
if (nombreLu==0) {nbZeros=nbZeros+1;}
nombreLu=Lecture.unEntier();
}
System.out.print("nombre de zros = ");
System.out.println(nbZeros);
}

Boucle pour faire n fois


On a souvent raliser une itration qui consiste simplement rpter n fois un traitement, n tant
connu avant de commencer litration.
Il peut sagir de calculer le nime lment dune suite rcurrente :
u=a
pour i=1 n faire u = f(u)

aprs litration, u vaut f (n)(a), n compositions dapplications de f.

Il peut galement sagir de traiter n donnes introduites par lecture, par exemple faire la somme de
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

97

Boucle pour faire n fois

12 valeurs entires lues au clavier :


somme = 0
pour i=1 12 faire nombreLu = lecture puis somme = somme+nombreLu
Un autre usage frquent de cette sorte ditration concerne le traitement des lments dun tableau.
Nous aurons loccasion de voir cela dans un chapitre ultrieur.
On peut bien videmment raliser ce genre ditration en utilisant la forme gnrale dj vue. Il suffit dintroduire dans la rcurrence llment supplmentaire i, de faon sarrter lorsque i=n.
Prenons comme exemple le calcul de f (n)(a) :
u = a, i = 0
tant que i<n faire u = f(u), i = i+1
qui gnre bien la suite dtats <u,i> = <a,0> <f(a),1> <f (2)(a), 2>... <f (n)(a), n>
ou, autre exemple, le calcul de la somme de 12 nombres lus au clavier :
somme = 0, i = 0
tant que i<12 faire
nombreLu = =lecture puis somme = somme+nombreLu, i = i+1
Cependant il est plus clair dutiliser la forme particulire bien adapte cette sorte ditration appele boucle pour.

La syntaxe de cette boucle en Java est :


for

(int

i=0;

i<n;

i=i+1)

{ instructions

Cette forme est strictement quivalente :


{ int i=0; while (i<n) {

instructions

i=i+1;}

Remarque : la construction for ouvre un bloc qui permet davoir une variable int ilocale ce
bloc, qui na dexistence que pendant la dure dexcution de litration. Nous avons nomm i
cette variable, mais bien sr tout autre identificateur est acceptable.
On peut ainsi programmer les deux exemples prcdents :
Calcul de f n(a) :
u=a; for (int i=0; i<n; i=i+1) {u=f(u);}
Somme de 12 nombres lus au clavier :
somme=0;
for (int i=0; i<12; i=i+1) {
int nombreLu=Lecture.unEntier();
somme=somme+nombreLu;
}

98

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

Instructions dincrmentation et dcrmentation : i++, i-Le besoin dincrmenter (ajouter 1) ou de dcrmenter (retrancher 1) une variable tant trs frquent, Java, comme C et C++, possde une forme abrge pour raliser ces oprations :
i++; est peu prs1 quivalent i=i+1;
i--; est peu prs quivalent i=i-1;
Avec cette notation pour lincrmentation, la boucle prcdente scrit donc :
for (int i=0; i<12; i++) {...}
Tout le monde utilise ces formes abrges : elles sont pratiques et amliorent la lisibilit si on les
utilise sans abus, cest dire juste pour incrmenter ou dcrmenter une variable qui joue un rle
de compteur.

Forme gnrale de la boucle pour


La boucle for de Java est plus gnrale que la forme prconise ci-dessus. La forme gnrale est :
for ( initialisation; condition; progression) { instructions }
dans laquelle :

initialisation est une instruction quelconque destine initialiser ltat du contrle de litra-

tion,
condition est une expression boolenne qui conditionne la continuation de litration,
progression est une instruction quelconque destine faire progresser ltat du contrle de litration.
La smantique exacte est donne par la forme strictement quivalente utilisant un while :
{
}

initialisation;
while ( condition) { instructions

progression;}

On dconseille dabuser de cette forme gnrale. Elle nest ni plus courte ni plus puissante que la
forme while . Cest un simple chamboulement textuel des rubriques de litration.

1. Les diffrences sont que dans la forme i++ i nest value quune fois, ce qui peut conduire un rsultat diffrent si i
est une expression de dsignation complique, et en tant quexpression i++ vaut i avant incrmentation alors que
i=i+1 vaut la valeur de i aprs que lincrmentation soit faite.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

99

Boucles rsultat polymorphe

6.8

Boucles rsultat polymorphe


objectif : concevoir une itration dont le rsultat peut prendre plusieurs formes.
On a parfois besoin de calculer par une itration un rsultat qui peut prendre plusieurs formes, cest-dire qui ne sexprime pas de faon naturelle par une valeur ni par une composition de valeurs de
types dtermins. Le rsultat peut tre dun type ou dun autre type selon les donnes. Un tel rsultat qui peut avoir plusieurs formes, donc plusieurs types, est dit polymorphe.
Exemple de boucle rsultat polymorphe :
On recherche dans une squence de nombres le premier nombre ayant une certaine proprit, par
exemple le premier nombre pair. Sil ny a aucun nombre pair dans la squence, le rsultat est de la
forme pas de nombre pair, sil y a effectivement un nombre pair, le rsultat est de la forme le
premier nombre pair vaut telle valeur.
Pour programmer une telle itration, une mthode consiste utiliser une variable auxiliaire pour
indiquer la forme effective du rsultat obtenu. Cette variable est dun type numr dont chaque
valeur correspond une des formes du rsultat. Dans notre exemple, cette variable, quon pourra
appeler etatRecherche, aura 3 valeurs possibles : {nonDcid, absent, prsent}. La valeur initiale
de la variable etatRecherche est nonDcid, ce qui signifie que la squence de donnes lues jusqu
prsent ne permet pas de dcider du rsultat. Dans notre exemple, etatRecherche vaut nonDcid
tant que lon na pas rencontr de nombre pair ni la marque de fin.

Pour la squence de donnes 3 56 7 9 8 -1, la suite des tats des variables de boucle sera :
i:
0
tatRecherchei : nonDcid
nombreLui
3

nonDcid
56

prsent
56

Le rsultat est la paire tatRecherche=prsent, nombreLu=56.


Pour la squence de donnes 1 3 5 7 -1, la suite des tats des variables de boucle sera :
i:
0
tatRecherchei : nonDcid
nombreLui :
1

nonDcid
3

nonDcid
5

nonDcid
7

nonDcid
-1

absent
-1

Le rsultat est tatRecherche=absent. La variable nombreLu na pas de valeur significative (elle


vaut -1, mais cest sans importance).

100

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

La gnralisation de ces scnarios conduit au programme suivant :


class ChercheNombrePair {
enum Etat {nonDecide,absent,present}

public static void main(String[] arg) {


Etat etatRecherche=Etat.nonDecide;
int nombreLu=Lecture.unEntier();
while (etatRecherche==Etat.nonDecide) {
if (nombreLu==-1) {etatRecherche=Etat.absent;}
else if (nombreLu%2==0) {etatRecherche=Etat.present;}
else {nombreLu=Lecture.unEntier();}
}
switch (etatRecherche){
case absent
: System.out.println("aucun nombre pair"); break;
case present
: System.out.print("premier nombre pair = ");
System.out.println(nombreLu);
}
}

Remarque : pour citer une valeur de type numr en Java, il faut gnralement prfixer lidentificateur de la valeur par le nom du type (Etat.nonDecide ) sauf pour lusage dans un switch o
on indique simplement lidentificateur de la valeur (case absent:). Le type de largument du
switch indique sans ambigut de quel type numr il sagit.
Dans le cas relativement frquent dune boucle de recherche, comme cest le cas ici, on peut utiliser une programmation ad-hoc qui utilise une variable boolenne initialise faux et qui est
affecte vrai quand ce que lon cherche est trouv.
La condition darrt de la boucle est alors les donnes sont toutes visites ou on a trouv. Pour
lexemple prcdent, en appelant present cette variable boolenne, cela donne :
class ChercheNombrePair {
public static void main(String[] arg) {
boolean present=false;
int nombreLu=Lecture.unEntier();
while (!present && nombreLu!=-1) {
if (nombreLu%2==0) {present=true;}
else {nombreLu=Lecture.unEntier();}
}
if (present) {
System.out.print("premier nombre pair = ");
System.out.println(nombreLu);
}
else /*!present*/{
System.out.println("aucun nombre pair");
}
}
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

101

Boucles rsultat polymorphe

Remarque sur les conditions complexes


On a souvent besoin dexprimer des conditions darrt compliques, qui ne pas par un simple test
dgalit ou autre. Cest le cas dans lexemple prcdent : les donnes sont toutes visites ou on a
trouv. Une telle condition sexprime en utilisant les oprateurs logiques usuels :
le et (&&), le ou (||) et la ngation (!)
Il est impratif de savoir manipuler ces oprations logiques. Par exemple, si cond est une condition
darrt dune itration, la condition indiquer dans le while est sa ngation !cond. Il faut donc
connatre les lois logiques qui permettent dexprimer de diffrentes faons la ngation dune condition complexe. Ce sont les lois de De Morgan :
non(A ou B) = non A et non B

non(A et B) = non A ou non B

soit en Java :
!(A || B) = !A &&

!B

!(A && B) = !A ||

!B

Cest pourquoi dans lexemple prcdent, la condition du while a t crite :


!present && nombreLu!=-1
on aurait pu aussi bien lcrire (question de got) :
!(present || nombreLu=-1)

102

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

QCM 6.1
Considrons les fonctions :
public static double p1(double x, int k){
// prrequis
: k>=0
// rsultat
: ???
double resul=1;
int i=0;
while(i<k){resul=resul*x; i++;}
return resul;
}
public static double p2(double x, int k){
// prrequis
: k>=0
// rsultat
: ???
if(k==0) {return 1;}
else {return x*p2(x,k-1);}
}

1 - Les fonctions p1 et p2 sont quivalentes (rendent des rsultats identiques).


2 - Les fonctions p1 et p2 ne sont pas quivalentes (ne rendent pas des rsultats identiques).
3 - La fonction p1 est rcursive.
4 - La fonction p2 est rcursive.
5 - La fonction p1 est itrative.
6 - La fonction p2 est itrative.
7 - p1(10,5) vaut 50.
8 - p2(10,5) vaut 50.
9 - p1(10,5) vaut 100000.
10 - p2(10,5) vaut 100000.
11 - p1(2,5) vaut 10.
12 - p2(2,5) vaut 64.
13 - p1(1,5) vaut 5.
14 - p2(1,5) vaut 1.
15 - p1(-1,5) vaut -5.
16 - p2(1,5) vaut -1.
17 - La fonction p1 rend en rsultat x fois k.
18 - La fonction p1 rend en rsultat x la puissance k (x multipli k fois par lui-mme).
19 - La fonction p1 ne termine pas toujours.
20 - La fonction p2 rend en rsultat x fois k.
21 - La fonction p2 rend en rsultat x la puissance k (x multipli k fois par lui-mme).
22 - La fonction p2 ne termine pas toujours.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

103

Boucles rsultat polymorphe

QCM 6.2
Considrons la fonction :
static int log(int b,int n){
// prrequis : b>=2, n>0
// rsultat : la partie entire du logarithme base b de n
int resul=0; int nn=n;
while(nn!=1){
nn=nn/b; resul++;
}
return resul;
}
Rappel : la partie entire du logarithme base b de n est gal au nombre de chiffres significatifs de
lcriture en base b de n moins 1. On doit avoir par exemple log(10,3675)=3, car
10003675<10000.

1 - log(10,10000) rend 4.
2 - log(10,10000) rend 1.
3 - log(10,10000) ne termine pas.
4 - log(2,4) rend 2.
5 - log(2,32) rend 5.
6 - log(2,32) ne termine pas.
7 - log(10,3675) rend 3.
8 - log(10,3675) ne termine pas.
9 - Cette fonction est correcte (satisfait sa spcification).
10 - Cette fonction est incorrecte car ne termine pas toujours.

104

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

Exercices
objectif : le but des 3 exercices qui suivent est de
rsoudre un problme de manire rcursive en trouvant de bonnes quations,
rsoudre un problme de manire itrative en imaginant une suite rcurrente et en testant cette suite sur des exemples,
savoir prouver quune itration est correcte en exhibant un invariant et une fonction de
terminaison.
Dans les trois exercices qui suivent, on sintresse aux chiffres de lcriture dcimale dun nombre
entier. Par exemple lentier n=3827 est reprsent en dcimal par la suite de chiffres :
7 2 8 3
Ici, un chiffre dcimal sera simplement un entier compris entre 0 et 9.
Le premier chiffre est le chiffre de poids faible, ou chiffre de rang 0. Il sobtient comme reste de la
division du nombre par 10 : n%10 = 7
Les chiffres restants sont les chiffres du quotient du nombre par 10 : n/10=382.
Ceci nous permet dutiliser un nombre entier comme une suite de donnes, ses chiffres dcimaux.
Cest un petit subterfuge qui nous permet de nous passer provisoirement de structures de donnes
mieux adaptes telles que les tableaux, les chanes de caractres, les listes...

Exercice 6.1

Rcursivit - itration : ime chiffre dcimal dun nombre

tant donn un entier n>=0, on souhaite trouver le ime chiffre de son criture dcimale. Les chiffres sont numrots partir de 0 depuis les poids faibles. Si i est plus grand que le rang du plus fort
chiffre significatif, le rsultat sera 0.
Exemple : pour n=245
chiffre( 0,n) vaut 5,
chiffre( 1,n) vaut 4,
chiffre( 2,n) vaut 2,
chiffre( 3,n) vaut 0...
Rappels : le chiffre dcimal de rang 0 de n est le reste de la division de n par 10 (n modulo 10,
n%10 en java). les autres sont ceux du quotient entier de n par 10 (n/10 en Java).
Exemple : 245%10 vaut 5, 245/10 vaut 24

Rdiger la fonction chiffre de manire rcursive. On commencera par indiquer les quations

qui justifient cette dfinition rcursive.


Rdiger la fonction chiffre de manire itrative. On commencera par envisager une suite qui
conduise la solution et on testera cette suite sur quelques scnarios.
Justifier lexactitude de lalgorithme itratif au moyen dun invariant de boucle.
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

105

Boucles rsultat polymorphe

Exercice 6.2

Rcursivit - itration : somme des chiffres dcimaux dun nombre

On considre la fonction SommeChiffres qui vaut la somme des chiffres de lcriture dcimale dun
entier positif ou nul.
exemple : SommeChiffres(582) = 2 + 8 + 5 = 15

Rdiger cette fonction de manire rcursive (fonction SommeChiffresRec ). On commen-

cera par indiquer les quations qui justifient cette dfinition rcursive.
Rdiger cette fonction de manire itrative (fonction SommeChiffresIter ). On commencera par envisager une suite qui conduise la solution et on testera cette suite sur quelques scnarios.
Indiquer linvariant de boucle et une fonction de terminaison de la version itrative.

Exercice 6.3

Rcursivit - itration : inclusion des chiffres dcimaux

tant donns deux entiers m et n, on souhaite dterminer si lcriture dcimale de m contient celle
de n. On suppose que m >= 0, n >= 0. On suppose galement que m < 231-1, de sorte quil peut tre
reprsent par une valeur de type int en Java.
Exemple : si m = 9413 et n = 41 alors contient( m,n) est vrai.
Pour raliser cette fonction, on a besoin dune fonction auxiliaire plus simple, termine( m,n)
qui vaut vrai si lcriture dcimale de n est gale la fin de celle de m et faux sinon. Exemples :
termine( 941,41) est vrai, termine( 9418,41) est faux.
Version rcursive :

Rdiger la fonction termine de manire rcursive.


Rdiger la fonction contient de manire rcursive.
Rdiger une procdure principale qui teste contient et termine sur des exemples pertinents.

Version itrative :

Refaire la mme chose en programmant les fonctions termine et contient de manire itrative.
Indiquer les invariants de boucles qui justifient lexactitude de ces itrations.

Exercice 6.4

Itration : plus grand commun diviseur

objectif : savoir grer la malencontreuse squentialit des affectations dans un corps de boucle.
Rdiger une fonction qui calcule le pgcd de deux nombres a et b les strictement positifs en utilisant
les proprits suivantes :
avec a>b : si le reste de la division de a par b est nul, b est le pgcd, sinon le pgcd de a et b est gal
au pgcd de b et du reste. Exemple :
pgcd(310,21) = pgcd(21,12) = pgcd(12,9) = pgcd(9,3). Ici 3 divise 9, le pgcd est donc 3.

106

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

Exercice 6.5

Itration : Rsolution dune quation

objectif : savoir utiliser des identifications auxiliaires pour viter de calculer plusieurs fois la
mme chose.
On se propose de trouver une solution de lquation x = cos(x) entre 0 et /2.
Pour cela on peut calculer les termes de la suite :
x0 = 0, xi+1 = cos(xi) pour i>0
Cette suite converge vers la solution, comme le suggre le schma suivant :

x
cos(x)

Rdiger la fonction qui calcule cette solution epsilon prs, epsilon tant pass en paramtre.
Pour calculer le cosinus, on dispose de la fonction : double Math.cos(double x) .

Exercice 6.6

Itration : calcul d'une valeur approche de

objectif : savoir transformer une itration pour la rendre plus efficace en utilisant des variables
auxiliaires contenant les valeurs des expressions coteuses calculer.
On peut calculer une valeur approche de l'aide de la srie :
n

lim 4
n

i=0

i
1
-------------- = 4 1 1--- + 1--- 1--- +

2i + 1
3 5 7

Les termes de la srie tant alternativement positifs et ngatifs, et strictement dcroissants en valeur
absolue, la srie converge par valeurs alternativement suprieures et infrieures, et le n-ime terme
est un majorant de l'erreur commise en arrtant le calcul au rang n.
Rdiger et tester une fonction calculPi calculant epsilon prs, epsilon tant donn en paramtre.
Premire version :

i
1
------------Dans cette version on calculera chaque terme de la srie comme lindique la formule
2i + 1
en calculant -1i au moyen de la fonction Math.pow(-1,i) dont le rsultat est un nombre rel.

Seconde version :
Dans cette version, on cherchera viter les calculs de -1i (alternance des signes) et de 2i+1 en les
remplaant par des oprations juges moins coteuses.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

107

Boucles rsultat polymorphe

Exercice 6.7

Boucle pour : fonction puissance

objectif : savoir utiliser la boucle pour.


Rdiger une fonction qui calcule an (a la puissance n, a nombre rel et n nombre entier positif ou
nul), en utilisant la multiplication.

Exercice 6.8

Rcurrence du second ordre : suite de Fibonacci

objectif : savoir transformer une rcurrence du second ordre en rcurrence du premier ordre.
La suite de Fibonacci est dfinie par la relation suivante :
fibo(0) = 0
fibo(1) = 1
fibo(n) = fibo(n-2) + fibo(n-1) pour n > 1
On se propose de rdiger une fonction int
suite.

fibo(int

qui
n) calcule le nime terme de cette

La dfinition utilise une rcurrence du second ordre (le rang n dpend des rangs n-1 et n-2). Nous
savons calculer par une itration les termes dune rcurrence du premier ordre. Comment faire pour
le second ordre (ou tout ordre suprieur) ?
Principe gnral : on se ramne une rcurrence du premier ordre, mais vectorielle. Ici cela
devient :

fiboNMoins1 i
fiboN i

fiboN i 1

fiboNMoins1 i 1 + fibo N i 1

Appliquer ce principe pour programmer fibo de manire itrative.

108

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

objectif : Les 5 exercices qui suivent concernent des itrations sur entres de donnes. Le but
principal est de placer convenablement les instructions de lecture.

Exercice 6.9

Itration sur entres de donnes : moyenne

Rdiger un programme qui lit une suite de nombres entiers positifs, termine par -1 et affiche la
moyenne de ces nombres.
Exemple : 7 3 0 4 -1
la moyenne est 3.5

Exercice 6.10

Itration sur entres de donnes : maximum dune squence de nombres

On entre au clavier une suite non vide dentiers positifs termine par la marque -1. Rdiger un programme qui lit ces nombres et calcule le maximum de la suite.
Exemple : 7 3 0 4 1 8 23 0 9 -1
le maximum est 23

Exercice 6.11

Itration sur entres de donnes : plus longue sous suite constante

On entre au clavier une suite non vide dentiers positifs termine par la marque -1. Rdiger un programme qui lit ces nombres et calcule la longueur de la plus longue sous-suite constante.
Exemple : 7 5 5 4 1 1 1 1 8 8 4 -1
longueur de la plus longue sous-suite constante = 4 (suite de quatre 1).

Exercice 6.12

Nombre de "le"

Rdiger un programme qui affiche le nombre de paires de caractres successifs "le" dans un texte
lu au clavier et termin par un point.

Exercice 6.13

Nombre de mots

Rdiger un programme qui dtermine et affiche le nombre de mots contenus dans une phrase termine par un point. On considre que le caractre sparateur de mots est le caractre espace.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

109

Boucles rsultat polymorphe

Rcursivit - itration : ime chiffre dcimal dun nombre

Aide 6.1

Version rcursive : utiliser les quations suivantes

pour i=0, chiffre(i,n) = n%10


pour i>0, chiffre(i,n)=chiffre(i-1,n/10)

Version itrative :
Voici un exemple de scnario, pour la recherche du chiffre de rang 3 dans 24593 :
ii

nn

24593

2459

245

24

Aide 6.2

Rcursivit - itration : somme des chiffres dcimaux dun nombre

Version rcursive : utiliser les quations suivantes

Pour n=0, la somme des chiffres est nulle.


Pour n>0, la somme des chiffres est la somme du chiffre de poids faible et de la somme des chiffres du nombre restant une fois enlev le chiffre de poids faible. Exemple :
sommeChiffres(285) = 5 + sommeCchiffres(28)
Nous avons donc les quations :

pour n=0, sommeChiffres(n) = 0


pour n>0, sommeChiffres(n) = n%10 + sommeChiffres(n/10)

Version itrative :
Lide consiste cumuler les chiffres de n en commenant par les poids faibles.
Exemple pour n=285 :

110

somme

13

15

resteAVoir

285

28

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

Aide 6.3

Rcursivit - itration : inclusion des chiffres dcimaux

Version rcursive de termine :

si n==0, termine(m,n) est vrai (par convention).


si n>0 et les chiffres de poids faibles de n et m sont diffrents, termine(n,m) est faux.
Exemple :
termine(4576, 123) est faux
si n>0 et les chiffres de poids faibles de n et m sont gaux, termine(m,n) est vrai si et seulement
si le nombre constitus des poids plus forts de n termine le nombre constitu des poids plus forts
de m.
Exemples :
termine(4123, 123) est vrai
termine(4523, 123) est faux
Ce qui donne les quations :

si n==0, termine(m,n)=vrai
si n>0 et n%10m%10, termine(m,n)=faux
si n>0 et n%10m%10, termine(m,n)= termine(m/10,n/10)
Version rcursive de contient :

si n>m, contient(m,n) est faux.


Exemple : contient(87, 463) est faux
si nm et termine(m,n) est vrai, contient(n,m) est vrai.
Exemple :
contient(4123, 123) est vrai
si nm et termine(m,n) est faux, contient(m,n) vaut la mme chose que contient(m/10,n)
Exemples : contient (242356, 423) = contient (24235, 423) = contient (2423, 423) = vrai
contient(24585,423) = contient(2758,423) = contient(275,423) = faux
Ce qui donne les quations :

si n>m, contient(m,n) = faux.


si nm et termine(m,n), contient(m,n)=vrai.
si nm et !termine(m,n) est faux, contient(m,n)=contient(m/10,n)
Ces quations conduisent facilement aux versions rcursives de termine et de contient .

Version itrative de termine :


Le principe consiste comparer les chiffres de n et m partir des poids faibles.
Exemple de scnario pour lequel termine(m,n)=vrai :
mm

941

94

nn

41

Le rsultat vrai est atteint quand nn=0, ce qui signifie que lon a compar avec succs tous les chiffres de n avec ceux de m.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

111

Boucles rsultat polymorphe

Exemple de scnario pour lequel termine(m,n)=faux :


mm

9481 948

nn

41

Le rsultat faux est atteint quand les chiffres de poids faible de nn et mm sont diffrents.
Version itrative de contient :
Le principe consiste tester si les chiffres de n se rencontrent quelque part lintrieur des chiffres
de m.
Exemple de scnario pour lequel contient(m,n)=vrai :
mm

9413

941

41

41 --> rsultat vrai car termine(mm,n)

Exemple de scnario pour lequel contient(m,n)=faux :


mm

9413

941

94

43

43

43

43 --> rsultat faux car mm<n

Aide 6.4

Itration : plus grand commun diviseur

Les proprits indiques nous suggrent une suite de deux composantes, aa et bb, avec aabb. Il
faut se mfier que aa soit bien suprieur bb initialement. Pour cela il doit tre initialis avec la
plus grande valeur parmi a et b, et bb doit tre initialise avec lautre valeur. Le scnario suivant
illustre le calcule du pgcd de 21 et 390 :
aa

390

21

12

bb

21

12

Le pgcd est 3. Il est atteint quand bb=0.

Aide 6.9

Itration sur entres de donnes : moyenne

La moyenne est la somme divise par le nombre de nombres somms. On peut difficilement calculer la moyenne de faon itrative. Il faut calculer la somme et le nombre de nombres, puis aprs
litration calculer la moyenne. Exemple :
nombreLu : 7

-1

somme :

11

11

14

nbNombres : 0

puis : moyenne = 14/4 = 3.5

112

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Rcursivit et itration

Aide 6.10

Itration sur entres de donnes : maximum dune squence de nombres

En plus de la suite des valeurs lues, il faut avoir le maximum des donne lues depuis le dbut de la
squence. Le rsultat est bien videmment ce maximum une fois lue toute la squence.
Exemple :
nombreLu : 2

max :

Aide 6.11

-1

Itration sur entres de donnes : plus longue sous-suite constante

Nous envisageons une suite compose :

du nombre lu : nombreLu,
de la valeur de la sous-suite constante courante : valSousSuite,
de la longueur courante de la sous-suite constante courante : longueurSousSuite,
de la longueur maximum des sous-suites rencontres : longueurMax.
Voici un exemple de scnario :
nombreLu :

7 5 5 4 1 1 1 1 8 8 4 -1

valSousSuite :

7 5 5 4 1 1 1 1 8 8 4

longueurSousSuite :

1 1 2 1 1 2 3 4 1 2 1

longueurMax :

1 1 2 2 2 2 3 4 4 4 4

Aide 6.12

Nombre de "le"

Lide gnrale est davoir en permanence deux caractres successifs du texte, car1 et car2 et de les
comparer avec 'l' suivi de 'e' :
car1

car2

nombreDeLe

Remarque : Il faut initialiser car1 et car2 par deux lectures avant litration.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

113

Boucles rsultat polymorphe

Aide 6.13

Nombre de mots

Quelques prcisions : nous acceptons les textes de 0 caractre, et dans ce cas le nombre de mots et
0. Les mots peuvent tre spars par un nombre quelconque despaces.
On peut partir du principe que ce qui caractrise un mot est la fin du mot. Une fin de mot est
caractrise une paire de caractres dont le premier car1 nest pas un espace et le second car2 est
un espace :
le

corbeau
1

et le
2

renard
4

4 mots

Il ne faut pas oublier le cas o le dernier mot serait termin par le point final :
le

corbeau
1

et le
2

renard.
4

4 mots galement

Il y a un mot de plus la fin si car1 nest pas un espace aprs avoir lu le point final.

114

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Type numr, type structure, notion de rfrence

Type numr, type structure,


notion de rfrence

CHAPITRE 7

objectif : comprendre lintrt de dfinir de nouveaux types de donnes, spcifiques un


domaine dapplications. Nous verrons dans ce chapitre les types numrs et les structures. Les
structures au sens strict ne sont pratiquement plus utilises dans les langages modernes : elles
sont avantageusement remplaces par les classes qui permettent dassurer labstraction, notion
essentielle pour la comprhension et la fiabilit des logiciels. Les types classe seront vus dans
un chapitre ultrieur.
La simplicit des exemples choisis jusqu prsent a permis de reprsenter les donnes traites par
les algorithmes en utilisant uniquement les types prdfinis mis notre disposition : entier, rel,
caractre, etc... Il nen nest pas de mme lorsque les donnes reprsenter sont plus complexes.
Exemples :

On veut reprsenter les cartes dun jeu de 32 cartes. Il faut notamment reprsenter la force de

chaque carte, sept, huit, neuf, dix, valet, dame, roi, as, ainsi que sa couleur, trfle, carreau,
cur, pique. On peut videmment imaginer de reprsenter ces valeurs par des entiers, mais les
algorithmes deviennent alors totalement illisibles.
On veut reprsenter les clients dune socit de vente. Aucun des types prdfinis nest adapt
reprsenter par un seul objet, un nom, une adresse et un compte.
Dans chacun de ces cas, il va falloir inventer un nouveau type de donnes. Dans ce chapitre nous
allons tudier les plus simples des types programms : les types numrs et les types structures.

7.1

Type numr
objectif : approfondir lusage des types numrs.
Comme nous lavons dj vu, un type numr est dfini par lnumration de ses valeurs. Chaque
valeur est note au moyen dun identificateur. Par exemple, pour la couleur et la force dune carte
on peut dfinir en Java les types numrs :
enum CouleurDeCarte {trefle, carreau, coeur, pique}
enum ForceDeCarte {sept, huit, neuf, dix, valet, dame, roi, as}
Un type numr offre peu de proprits : dans les cas usuels, on na besoin que du test dgalit.
Dans certains cas, on peut vouloir une relation dordre, par exemple pour la force dune carte, on
peut souhaiter lordre sept<huit<neuf<dix<valet <dame<roi<as.
La relation dordre en Java est dfinie par lordre dnumration des identificateurs de valeurs. La

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

115

Type numr

comparaison se fait au moyen de la fonction compareTo . La syntaxe dutilisation de cette fonction est particulire : a et b tant des expressions dun mme type numr,
a.compareTo( b)
rend en rsultat un entier <0 si a est infrieur b, 0 si a est gal b et >0 si a est suprieur b.
Exemple : la fonction suivante indique si une couleur est rouge (carreau ou cur) :
static boolean estRouge(CouleurDeCarte c) {
// rsultat
: indique si c est rouge
return c==CouleurDeCarte.carreau || c==CouleurDeCarte.coeur;
}
On aurait pu galement lcrire, en utilisant compareTo :
static boolean estRouge(CouleurDeCarte c) {
// rsultat
: indique si c est rouge
return
c.compareTo(CouleurDeCarte.carreau)==0
|| c.compareTo(CouleurDeCarte.coeur)==0;
}
Type numr et entres-sorties
Les types numrs sont utiliss pour amliorer la lisibilit des programmes. Il peuvent galement
servir communiquer avec lextrieur, au moyen de lectures et dcritures. Les langages de programmation offrent rarement la possibilit de raliser des oprations dentres sorties en clair de
valeurs dun type numr. En revanche, Java le permet. La forme en clair des valeurs dun type
numr sont simplement les chanes de caractres de leurs identificateurs.
Il existe pour chaque type numr deux fonctions de conversion :

Lappel de fonction x.toString() rend en rsultat la chane de caractres correspondant la


valeur de x. Exemple :
CouleurDeCarte c = CouleurDeCarte.trefle;
System.out.println(c.toString()); affiche trefle
On peut crire plus directement :
System.out.println(c); affiche trefle
car dans ce cas le compilateur rajoute automatiquement le .toString() .
Lappel de fonction nomDuTypeEnumr.valueOf( s) rend en rsultat la valeur de type
numr ayant pour identificateur la chane de caractres s. Exemple :
CouleurDeCarte c = CouleurDeCarte.valueOf("trefle");
donne c la valeur CouleurDeCarte.trefle .
On peut ainsi obtenir facilement des valeurs de type numr par lecture au clavier :
CouleurDeCarte couleurChoisie;
couleurChoisie = CouleurDeCarte.valueOf(Lecture.chaine());

Si la chane de caractres ne correspond aucun identificateur de valeur du type numr, la


fonction valueOf provoque une erreur (une exception de type IllegalArgumentException ).
116

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Type numr, type structure, notion de rfrence

7.2

Type structure
objectif : apprendre caractriser des donnes complexes laide de composantes plus simples.

Un type structure permet de reprsenter des donnes ayant plusieurs caractristiques appartenant
chacune un domaine de valeurs, donc dun certain type. Cela correspond peu prs lide
mathmatique du produit cartsien de plusieurs domaines. Comme exemple on peut considrer :

Un point dans un plan : ses caractristiques sont labscisse et lordonne. Le type de labscisse

est un nombre rel et celui de lordonne galement.


Une carte jouer : ses caractristiques sont la force et la couleur. La force est de type numr
ForceDeCarte={sept, huit, neuf, dix, valet, dame, roi, as} et la couleur de type numr CouleurDeCarte= {trfle, carreau, cur, pique}.
Une personne : ses caractristiques sont le nom, lge et le sexe. Le nom est de type chane de
caractres, lge de type entier et le sexe de type numr Sexe={masculin, fminin}.

7.2.1

Dfinition dun type structure


Une structure se prsente donc comme une collection de rubriques, appeles champs, ou encore
composants. Chaque composant possde un type et est identifi par un nom :
structure Point = < rel x, rel y >
structure CarteAJouer = < ForceDeCarte force, CouleurDeCarte couleur >
structure Personne = < ChaneDeCaractres nom, entier ge, Sexe sexe >
Des valeurs de ces types pourraient tre :
Point origine = <0,0>, Point p = <1.0, 12.45>
CarteAJouer mistigri = <valet, trfle>
Personne toto = <toto, 14, masculin>
En Java, les types structure se programment comme un cas particulier dobjets de type classe qui
sera dcrit au chapitre 10. On peut dfinir ainsi les types prcdents :
class Point {
public double x; public double y;
}
class CarteAJouer {
public ForceDeCarte force; public CouleurDeCarte couleur;
}
class Personne {
public String nom; public int age; public Sexe sonSexe;
}
Le vocable public signifie que ces composants sont accessibles en dehors du texte de la classe.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

117

Type structure

Notion de constructeur
Lorsquon cre un objet de type structure, on a frquemment besoin de lui donner une valeur. On
verra plus loin quon peut modifier par affectation les champs dune structure, mais il est prfrable
de prvoir linitialisation globale de tous les champs ds la cration. Cela donne dans tous les cas
une meilleure lisibilit et cest mme ncessaire pour des structures dont les membres seraient des
constantes, cest--dire quon ne peut plus modifier une fois lobjet cr.
En Java, cette initialisation se fait au moyen dun constructeur que lon dfinit dans le texte de la
classe. Un constructeur se prsente un peu comme une procdure. Il doit imprativement porter le
mme nom que la classe. Il a des paramtres, mais ne rend aucun rsultat : son rle est dinitialiser
lobjet au moment de sa cration. Dans le cas dune structure, il est bon de programmer un constructeur qui a comme paramtres les valeurs initiales de tous les champs de la structure (on appelle
parfois cela le constructeur trivial). En reprenant les exemples prcdents, cela donne :
class Point {
public double x; public double y;
public Point(double sonX, double sonY) {x=sonX; y=sonY;}
}
class CarteAJouer {
public ForceDeCarte force; public CouleurDeCarte couleur;
public CarteAJouer(ForceDeCarte f, CouleurDeCarte c) {
force=f; couleur=c;
}
}
class Personne {
public String nom; public int age; public Sexe sonSexe;
public Personne(String n, int a, Sexe s) {
nom=n; age=a; sexe=s;
}
}
En Java, la cration dune structure se fait au moyen de lopration new suivie du constructeur dot
de paramtres effectifs. Avec ces constructeurs, on pourra crer des structures initialises en utilisant les formes suivantes :
Point origine = new Point(0,0); Point p = new Point(1.0,12.45);
CarteAjouer mistigri =
new CarteAJouer(ForceDeCarte.valet, CouleurDeCarte.trefle);
Personne toto = new Personne("toto",14,Sexe.masculin);
Les dclarations prcdentes supposent les dfinitions suivantes de types numrs :
enum CouleurDeCarte {trefle, carreau, coeur, pique}
enum ForceDeCarte {sept, huit, neuf, dix, valet, dame, roi, as}
enum Sexe {masculin, feminin}

118

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Type numr, type structure, notion de rfrence

7.2.2

Cration de structure - dsignation par rfrence


objectif : comprendre la cration dynamique dobjets et la dsignation par rfrence..
En Java, comme dans dautres langages objets, les structures sont un cas particulier dobjet et les
objets sont obligatoirement dsigns par rfrence.

7.2.2.1 Premire notion dobjet


Un objet est quelque chose qui ncessite dtre cr et qui est dot dune identit. De faon intuitive, un objet est comme une vraie chose. Par exemple, sur une table se trouvent deux verres :

Ils nont pas toujours exist, ils ont un jour t crs.


Ils ont des caractristiques qui constituent leur valeur : une hauteur, un diamtre, une couleur... Ils ont tous deux 10 cm de haut, 5 cm de diamtre et sont transparents. Mais bien que possdant les mme caractristiques, bien quils soient indiscernables par leur valeur, il ne sagit
pas du mme verre : il y a le verre du milieu de la table et le verre du coin de la table. Ils ont
chacun une identit.
le verre du milieu de la table
Un verre est un objet.

le verre du coin de la table

un verre

un autre verre

De ce point de vue, la notion dobjet soppose la notion de valeur. Une valeur est un lment
dun ensemble, au sens mathmatique. Par exemple 12 est une valeur, cest un lment de lensemble des entiers. 12 na jamais t cr (du moins pas au sens de la fabrication dun objet physique). Il na pas didentit : son existence est une existence mathmatique, elle ne se situe ni dans
le temps, ni dans lespace. 12 nest pas un objet.

7.2.2.2 Notion de rfrence


Une rfrence est une valeur qui dsigne un objet. On dit parfois une adresse. Dans lexemple
des deux verres, le verre du milieu de la table et le verre du coin de la table sont des rfrences.
Elles dsignent respectivement le verre du milieu de la table et le verre du coin de la table.
Une rfrence est en quelque-sorte le nom dun objet, mais contrairement aux identificateurs que
nous avons dj utiliss, les rfrences sont des valeurs manipulables par les programmes : on va
pouvoir en passer en paramtre, en rendre en rsultat, en mmoriser dans des variables... Une rfrence est un nom interne, en ce sens quil constitue une donne au mme titre quune valeur
entire par exemple. Par contre un identificateur est un nom externe qui na pas dexistence objective pendant lexcution.
Il existe une valeur particulire de type rfrence qui ne dsigne aucun objet. Cest la rfrence
rien, encore appele rfrence nulle. En Java elle se note :
null : rfrence qui ne dsigne rien
Du point de vue de la mise en uvre dans un ordinateur, un objet est reprsent par une succession
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

119

Type structure

de mots de mmoire qui contiennent les divers composants de lobjet. Chaque mot de la mmoire
de lordinateur est dsign par un numro quon appelle son adresse. La rfrence un objet est
simplement ladresse o est rang lobjet en mmoire.
rfrence
2568

adresse mmoire
2567
2568
2569
2570

valet
trefle

force

objet de type
couleur CarteAJouer

7.2.2.3 Cration dun objet


Un expression de cration permet de crer des objets. Par exemple, lexpression
crer une CarteAJouer = < valet, trfle >
cre un nouvel objet de type CarteAJouer dont la force est valet et la couleur trfle. Cette expression rend en rsultat la rfrence qui dsigne lobjet cr.
En Java, les objets sont toujours crs par une expression de cration dont loprateur est new.
Lexpression prcdente scrit :
new CarteAJouer(ForceDeCarte.valet,CouleurDeCarte.trefle)
Son effet est illustr par le schma suivant :
avant

rfrence
3822

aprs

force : valet
couleur : trefle
espace des objets

espace des objets

De faon concrte et proche de la ralisation en machine, de la place mmoire est rserve pour le
nouvel objet, dans lespace des objets, le constructeur est excut pour initialiser lobjet et
ladresse mmoire de lobjet, qui constitue la rfrence lobjet, est rendue en rsultat (3822 dans
lexemple de la figure).
La forme gnrale de la cration est :
new

nomDeLaClasse(paramtres du constructeur)

7.2.2.4 Dclaration de rfrences


En gnral, lorsquon cre un objet, il faut capter sa rfrence pour ensuite utiliser lobjet. On peut
le faire en dclarant des variables, des constantes ou des paramtres de type rfrence. Une dclaration de rfrence objet de type classe T scrit :
T nom;

120

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Type numr, type structure, notion de rfrence

Par exemple :
CarteAJouer mistigri; variable de type rfrence un objet de type CarteAJouer .
Ceci est une dclaration sans initialisation explicite. En Java, une telle variable est initialise
null, rfrence qui ne dsigne rien.
mistigri

null

On peut ensuite affecter cette variable, par exemple pour capter la rfrence un objet cr :
mistigri =
new CarteAJouer(ForceDeCarte.valet,CouleurDeCarte.trefle);
On peut galement, comme pour toute dclaration de variable, linitialiser lendroit de sa
dclaration :
CarteAJouer mistigri =
new CarteAJouer(ForceDeCarte.valet,CouleurDeCarte.trefle);

mistigri

force : valet
couleur : trefle
espace des objets

On peut galement dclarer des constantes de type rfrence, au moyen de lattribut final :
final T nom = expression;
Par exemple :
final CarteAJouer mistigri = new CarteAJouer(valet,trefle);
Dans ce cas on est sr que lidentificateur dsignera toujours le mme objet.
On peut aussi, comme avec tout autre type, avoir des paramtres de type rfrence et des rsultats
de fonction de type rfrence. Nous en aurons quelques exemples dans le paragraphe suivant.

7.2.3

Slection de champ
Les champs dune structure sont accessibles au moyen dune opration appele slection de champ.
Etant donne une rfrence objet obj qui possde un champ de nom c, lexpression :
obj.c
dsigne le champ c de obj.
En tant quexpression, elle vaut ce que vaut le champ : mistigri.force vaut valet .
Champs variables - champs constants

Un champ dclar sans aucun autre vocable peut sutiliser comme une variable que lon peut
affecter :
mistigri.couleur=pique; modifie la couleur de lobjet dsign par mistigri .
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

121

Type structure

Si on dsire quun champ ne soit pas modifiable, il suffit de le dclarer avec le vocable final .

Le champ ainsi dclar se comporte comme une constante. Il reoit sa valeur par le constructeur, au moment de la cration de la structure, et cette valeur est dfinitive :

class CarteAJouer {
public final ForceDeCarte force;
public final
CouleurDeCarte couleur;
public CarteAJouer(ForceDeCarte f, CouleurDeCarte c) {
force=f; couleur=c;
}
}
Avec cette dfinition de CarteAjouer , la force et la couleur dune carte ne sont pas modifiables. avec
mistigri =
new CarteAJouer(ForceDeCarte.valet,CouleurDeCarte.trefle);
la carte dsigne par mistigri est tout jamais le valet de trfle.

7.2.4

Comparaison des rfrences - comparaison des valeurs


La comparaison dgalit est dfinie pour les rfrences aux objets. Cest le seul oprateur de comparaison applicable entre rfrences. Deux rfrences sont gales si elles dsignent le mme objet.
En Java la comparaison des rfrences se note ==.
mistigri

force : valet
couleur : trefle

carte1

force : valet
couleur : trefle

carte2

Il faut bien remarquer quune telle comparaison rend vrai si et seulement si les deux rfrences sont
les mmes, cest--dire dsignent le mme objet. Elle rend faux si les rfrences dsignent des
objets diffrents, mme sils ont mme valeur. Par exemple, avec la situation illustre ci-dessus :
mistigri==carte1 vaut true
mistigri==carte2 vaut false
bien que les objets dsigns par mistigri et carte2 aient la mme valeur. Loprateur == sur
les objets ne compare pas les valeurs mais seulement les rfrences. Si on veut comparer les
valeurs, il faut le programmer, le langage ne loffre pas.
Voici comment programmer la comparaison dgalit de valeur pour le type CarteAJouer , en
considrant que deux cartes sont gales si et seulement si elles ont mme force et mme couleur.
Cette comparaison, frquente pour des structures, sappelle une comparaison champ par champ.
Une convention trs rpandue est dappeler equals la fonction de comparaison dgalit de valeur
entre objets.

122

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Type numr, type structure, notion de rfrence

static boolean equals(CarteAJouer c1, CarteAJouer c2) {


// rsultat
: indique si les c1 et c2 ont mme valeur
return (c1.force==c2.force) && (c1.couleur==c2.couleur);
}

7.2.5

Rfrences en paramtre et en rsultat


Un paramtre ou un rsultat de type rfrence se note simplement en indiquant le type dobjet rfrenc. Prenons comme exemple une fonction laGagnante qui, tant donnes deux cartes jouer
rend en rsultat la carte gagnante. Les paramtres sont deux rfrences des cartes et le rsultat est
galement une rfrence une carte. Le rsultat est la rfrence la carte la plus forte, selon la force
des cartes, puis en cas dgalit des forces, selon la couleur des cartes. En cas dgalit des cartes,
cest--dire si les deux cartes ont mme force et mme couleur, le rsultat est la rfrence null.
static CarteAJouer laGagnante(CarteAJouer c1,CarteAJouer c2) {
// rsultat
: la plus forte des cartes c1 et c2
// null si les cartes sont de forces gales
if (c1.force.compareTo(c2.force)>0) {return c1;}
else if (c1.force.compareTo(c2.force)<0) {return c2;}
else /* forces gales, on compare les couleurs */ {
if (c1.couleur.compareTo(c2.couleur)>0) {return c1;}
else if (c1.couleur.compareTo(c2.couleur)<0) {return c2;}
else /* carte gales */ {return null;}
}
}

7.2.6

Exemple rcapitulatif
Le programme suivant gre un jeu de bataille simplifi entre deux joueurs. chaque coup chaque
joueur fournit la force et la couleur de sa carte, en clair au clavier. Si une carte est gagnante, le
joueur correspondant marque un point supplmentaire. En cas dgalit des cartes, aucun joueur ne
marque de point. La partie dure 5 coups et le score final est affich.
class JeuDeBataille {
static CarteAJouer laGagnante(CarteAJouer c1,CarteAJouer c2) {
// rsultat
: la plus forte des cartes c1 et c2
// null si les cartes sont de forces gales
if (c1.force.compareTo(c2.force)>0) {return c1;}
else if (c1.force.compareTo(c2.force)<0) {return c2;}
else /* forces gales, on compare les couleurs */ {
if (c1.couleur.compareTo(c2.couleur)>0) {return c1;}
else if (c1.couleur.compareTo(c2.couleur)<0) {return c2;}
else /* carte gales */ {return null;}
}
}
//...

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

123

Type structure

public static void main(String[] arg) {


int pointsDuJoueur1=0;
int pointsDuJoueur2=0;
for(int i=0; i<5; i++) {
System.out.print("joueur 1
: ");
CarteAJouer carte1 = new CarteAJouer(
ForceDeCarte.valueOf(Lecture.chaine()),
CouleurDeCarte.valueOf(Lecture.chaine()));
System.out.print("joueur 2
: ");
CarteAJouer carte2 = new CarteAJouer(
ForceDeCarte.valueOf(Lecture.chaine()),
CouleurDeCarte.valueOf(Lecture.chaine()));
CarteAJouer carteGagnante = laGagnante(carte1,carte2);
if (carteGagnante==carte1) {
System.out.println("le joueur 1 marque un point");
pointsDuJoueur1++;
}
else if (carteGagnante==carte2) {
System.out.println("le joueur 2 marque un point");
pointsDuJoueur2++;
}
else /*carteGagnante==null*/ {
System.out.println("personne ne marque");
}
}
System.out.println("score final");
System.out.print("joueur 1
:");
System.out.print(pointsDuJoueur1);
System.out.print(" joueur 2
:");
System.out.print(pointsDuJoueur2);
System.out.println();
}

Remarque : dans ce programme, deux nouvelles cartes sont cres par linstruction new chaque
tape du jeu. Cela peut sembler coteux en utilisation de la mmoire. Il ne faut pas trop se tracasser
de cela car le langage utilis, Java, dispose dun mcanisme de gestion de mmoire qui rcupre la
mmoire occupe par les objets qui ne servent plus, ce qui est le cas des anciennes cartes lorsquon
en cre des nouvelles chaque itration. Ce mcanisme de rcupration de mmoire sappelle un
ramasse-miettes, ou encore un garbage collector en anglais.

124

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Type numr, type structure, notion de rfrence

QCM 7.1
On souhaite dfinir une structure qui regroupe les caractristiques de chaque employ dune entreprise. Ces caractristiques sont le nom, le prnom, et la nature du poste occup. Les nature de
poste sont technique, administratif ou commercial.
La nature du poste est une valeur du type numr ainsi dfini :
enum NatureDePoste{technique, administratif, commercial}

1 - Une bonne dfinition de la structure envisage est :


class Employe{
public static String nom;
public static String prenom;
public static NatureDePoste emploi;
public Employe(String n, String p, NatureDePoste e){
nom=n; prenom=p; emploi=e;
}
}

2 - Une bonne dfinition de la structure envisage est :


class Employe{
public String nom;
public String prenom;
public NatureDePoste emploi;
public Employe(String n, String p, NatureDePoste e){
nom=n; prenom=p; emploi=e;
}
}

3 - Pour crer un objet de type Employe et capter sa rfrence par lidentificateur durand , il
faut crire :
Employe durand =
new Employe("Durand","Alfred",NatureDePoste.commercial);

4 - Pour crer un objet de type Employe et capter sa rfrence par lidentificateur durand , il
faut crire :
Employe("Durand","Alfred",NatureDePoste.commercial) durand;

5- Avec la (bonne) dfinition de la classe Employe , et la (bonne) cration de durand , le nom


de durand sobtient par lexpression :
durand.nom

6- Avec la (bonne) dfinition de la classe Employe , il est impossible de modifier la nature du


poste dun employ.
7 - Avec la (bonne) dfinition de la classe Employe , on peut modifier la nature du poste dun
employ. Pour attribuer durand la nature de poste administratif, il suffit dexcuter :
durand=Employe(administratif);

8 - Avec la (bonne) dfinition de la classe Employe , on peut modifier la nature du poste dun
employ. Pour attribuer durand la nature de poste administratif, il suffit dexcuter :
durand.emploi=NatureDePoste.administratif;

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

125

Type structure

QCM 7.2
La structure suivante dcrit les caractristiques dun produit vendu dans un magasin :
class DescriptifProduit{ // descriptif dun produit
public String nomProduit;
public String nomFournisseur;
public double prixDAchat;
public double tauxTVA;
public double margeBeneficiaire;
public DescriptifProduit(String nP,String nF,
double pA,double tva,double m){
}
}

1 - Pour crer le descriptif dune lessive, on peut crire :


new DescriptifProduit("lessiveMachin","machin",12,19.6,0.2)

2 - Pour crer le descriptif dune lessive, on peut crire :


DescriptifProduit
lessiveMachin("lessiveMachin","machin",12,19.6,0.2)

3 - Pour crer le descriptif dune lessive et capter sa rfrence par la variable lessiveMachin
on peut crire :
DescriptifProduit
("lessiveMachin","machin",12,19.6,0.2)

lessiveMachin;

4 - Pour crer le descriptif dune lessive et capter sa rfrence par la variable lessiveMachin
on peut crire :
DescriptifProduit lessiveMachin =
new DescriptifProduit("lessiveMachin","machin",12,19.6,0.2);

5 - Le nom du fournisseur de lessiveMachin

sobtient par lexpression :

nomFournisseur(lessiveMachin)

6 - Le nom du fournisseur de lessiveMachin

sobtient par lexpression :

lessiveMachin.nomFournisseur

7 - la fonction suivante :
static double prixDeVente(DescriptifProduit d){
double prixHorsTaxe = prixDAchat*(1+margeBeneficiaire);
return prixHorsTaxe*(1+tauxTVA);
}
est syntaxiquement correcte et rend en rsultat le prix de vente du produit dcrit par d.

8 - la fonction suivante :
static double prixDeVente(DescriptifProduit d){
double prixHorsTaxe = d.prixDAchat*(1+d.margeBeneficiaire);
return prixHorsTaxe*(1+d.tauxTVA);
}
est syntaxiquement correcte et rend en rsultat le prix de vente du produit dcrit par d.

126

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Type numr, type structure, notion de rfrence

Exercice 7.1

Reprsentation des points du plan au moyen dune structure

objectif : savoir utiliser les champs dune structure et crer un rsultat de type structure.
Un point du plan peut tre reprsent par ses coordonnes cartsiennes (x, y) dans un repre arbitraire. Dfinir une telle structure Point et rdiger les fonctions suivantes :

Distance entre deux points :


static double distance(Point a, Point b)
// rsultat
: la distance entre a et b
Surface du rectangle dfini par deux coins :
static double surfaceRectangle(Point a, Point b)
// rsultat
: la surface du rectangle dfini par les deux coins
// diagonalement opposs a et b
Point milieu de deux points :
static Point milieu(Point a, Point b)
// rsultat
: le point milieu entre a et b
Tester ces fonctions sur les points a=(5,4) et b=(1,2).
a

a
milieu

distance
b

surfaceRectangle
b

Vrifier quon a bien les galits :


distance(a,b) = 2distance(a,milieu(a,b) )
surfaceRectangle(a,b) = 4surfaceRectangle(a,milieu(a,b))

Exercice 7.2

Reprsentation des nombres complexes au moyen dune structure

objectif : savoir utiliser les champs dune structure, crer un rsultat de type structure, dfinir
une constante de type structure.
Un nombre complexe peut tre reprsent au moyen dune structure deux champs, x (partie
relle) et y (partie imaginaire).
Dfinir cette reprsentation au moyen dune classe Complexe .

Rdiger la fonction add qui rend en rsultat la somme de deux complexes.


Rdiger la fonction mul qui rend en rsultat le produit de deux complexes.
Rdiger une fonction
String toString(Complexe z)
qui rend en rsultat la chane de caractres de la forme x+y.i qui dnote le nombre complexe
z sous la forme usuelle. On fera en sorte de rendre simplement x pour x+0.i, y.i pour
0+y.i, x+i pour x+1.i, i pour 0+i et 0 pour 0+0.i .
Rdiger une procdure principale qui lit deux rels x et y, cre le nombre complexe z=x+y.i et
calcule et affiche le nombre complexe z.(z.(z+1)+1). Remarque : il faudra dfinir le complexe
1 (alias 1+0.i ) pour le faire participer aux oprations.
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

127

Type structure

Exercice 7.3

Reprsentation des liens de parent entre personnes

objectif : comprendre la smantique et lintrt de la dsignation par rfrence.


On dsire reprsenter des liens de parent entre personnes. Une personne possde un nom, un prnom, une adresse, un pre et une mre. Le nom, le prnom et ladresse sont des chanes de caractres. Le pre et la mre sont des personnes.
Le type Personne qui reprsente les personnes est ce quon appelle un type rcursif : sa dfinition sutilise elle-mme. La dsignation par rfrence permet de raliser ceci facilement. Un objet
de type Personne possdera deux champs, pere et mere, qui seront des rfrences aux objets
Personne qui reprsentent le pre et la mre. Le schma suivant illustre une collection de personnes avec leurs liens de parent :

nom : seize
prenom
: louis
adresse
: versailles
pere :
nom : antoinette
mere :
prenom
: marie
adresse
: versailles
pere :
mere :

nom : seize
prenom
: louisette
adresse
: versailles
pere :
mere :

nom : bonaparte
prenom
: napolon
adresse
: bastia
pere :
nom : beauharnais
mere :
prenom
: josphine
adresse
: bastia
pere :
mere :

nom : durand
prenom
: alfred
adresse
: labas
pere :
mere :

nom : dupont
prenom
: jules
adresse
: ici
pere :
mere :

nom : martin
prenom
: raimond
adresse
: ailleurs
pere :
mere :

1 - Rdiger la classe Personne qui reprsente de telles personnes. On prvoira deux


constructeurs :

un constructeur avec les 5 paramtres correspondant aux 5 attributs dune personne,


un constructeur avec seulement les 3 paramtres nom, prnom et adresse, pour crer une personne sans pre ni mre (ou dont on ignore qui est le pre et la mre : cest les cas de louis
seize, marie antoinette, napolon bonaparte et josphine beauharnais dans lexemple).

128

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Type numr, type structure, notion de rfrence

2 - Rdiger les fonctions :


static boolean sontDesFreres(Personne p1, Personne p2)
// rsultat
: indique si p1 et p2 sont frres
// (ont mme pre et mme mre)
static boolean p1EstGrandPereDep2(Personne p1, Personne p2)
// rsultat
: indique si p1 est grand-pre de p2
// (est le pere du pere ou de la mere)
3 - Rdiger une procdure principale qui :

cre le rseau de personnes illustr prcdemment,


teste si jules dupont et alfred durant sont frres, teste si jules dupont et louis seize sont

frres, teste si louis seize est un grand-pre de raimond martin et teste si napolon bonaparte est un grand-pre de jules dupont,
affiche ladresse de napolon bonaparte,
modifie ladresse du grand-pre paternel de raimond martin et affiche ladresse de napolon
bonaparte (prvoir le rsultat de cet affichage).

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

129

Type structure

Aide 7.1

Reprsentation des points du plan au moyen dune structure

La reprsentation dun point possde deux composantes, labscisse x et lordonne y. Labscisse


dun points a sobtient par a.x et son ordonnes par a.y.
La distance entre deux points a et b de coordonnes respectives (a.x, a.y) et (b.x, b.y) est donne par
la formule de Pythagore-Euclide :
(a x b x) + (a y b y)
Les points tant reprsents par une structure Point dote dun constructeur habituel, pour crer
un point de coordonnes (5,4), il faut excuter la dclaration/instruction :
Point a = new Point(5,4);
La cration est galement sollicite dans la fonction milieu , pour crer la reprsentation du point
milieu.

Aide 7.2

Reprsentation des nombres complexes au moyen dune structure

Les nombres complexes seront reprsents par une structure Complexe 2 champs, x et y, dote
dun constructeur usuel. Une telle structure reprsente le nombre complexe x+y.i.
Les fonctions add, mul et toString seront rdiges dans une classe TestStructureComplexe dont le main est le programme de test. Voici sa forme gnrale :
import es.*;
class TestStructureComplexe {
static Complexe add(Complexe z1, Complexe z2) {
// rsultat
: somme de z1 et z2
...
}
static Complexe mul(Complexe z1, Complexe z2) {
// rsultat
: produit de z1 et z2
...
}
static String toString(Complexe z){
// rsultat
: la chane de caractre qui figure z en clair
...
}

130

public static void main(String[] u){


...
}

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Type numr, type structure, notion de rfrence

Les fonctions add et mul ncessitent la cration du rsultat. Ces cration utilisent le constructeur,
en appliquant les formules classiques de laddition et de la multiplication des nombres complexes :
pour laddition : (x1+y1.i) +(x2+y2.i) = x1+x2 + (y1+y2).i
pour la multiplication : (x1+y1.i)(x2+y2.i) = x1.x2 - y1.y2 +(x1.y2 + y1.x2).i
La fonction daddition par exemple est de la forme :
static Complexe add(Complexe z1, Complexe z2) {
// rsultat
: somme de z1 et z2
...
}
Ne pas oublier que les composantes de z1, par exemple, sobtiennent par z1.x et z1.y.
Pour la fonction toString , il est conseill dans un premier temps den faire une version simple
qui rend en rsultat une chane de caractres de la forme unique x+y*i, quelques soient x et y, nuls
ou non, valant 1 ou non... Cette fonction est importante pour visualiser le rsultat des tests.
Dans la procdure principale de test, nous devons utiliser le nombre complexe 1, qui nest autre
que 1+0.i. Nous devons dfinir pour cela la constante complexe UN :
final Complexe UN = new Complexe(1,0);

Aide 7.3

Reprsentation des liens de parent entre personnes

class Personne {
public String nom;
public String prenom;
public String adresse;
public Personne pere;
public Personne mere;
public Personne(String n, String p, String a,
Personne sonPere, Personne saMere){
// personne de nom n, prenom p, adresse a,
// pre sonPere et mre saMere
nom=n; prenom=p; adresse=a; pere=sonPere; mere=saMere;
}

public Personne(String n, String p, String a){


// personne de nom n, prenom p, adresse a, sans pre ni mre
nom=n; prenom=p; adresse=a; pere=null; mere=null;
}

Les champs pere et mere sont dclar de type Personne , ce qui, en Java, en fait des rfrences
des objets de type Personne .
Le constructeur pour des personnes sans pre ni mre initialise les champs pere et mere null,
rfrence qui ne dsigne rien.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

131

Type structure

Les fonctions sontDesFreres et p1EstGrandPereDep2 sont rdiger dans la classe


TestParents qui contient galement la procdure principale de test. Voici sa forme gnrale :
class TestParents {
static boolean sontDesFreres(Personne p1, Personne p2){
// rsultat
: indique si p1 et p2 sont frres
// (ont mme pre et mme mre)
...
}
static boolean p1EstGrandPereDep2(Personne p1, Personne p2){
// rsultat
: indique si p1 est grand-pre de p2
// (est le pre du pre ou de la mre)
....
}

public static void main(String[] z){


Personne louisSeize =
new Personne("seize","louis","versailles");
...
}

Pour les fonctions sontDesFreres et p1EstGrandPereDep2 il faut cependant se poser la


question de quel est le rsultat si certaines des personnes concernes nont pas de pre ou de mre.
Il semble naturel de considrer que pour avoir un frre, il faut avoir des parents, et pour avoir un
grand pre, il faut avoir un pre ou une mre.

132

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Chanes de caractres

CHAPITRE 8

Chanes de caractres

objectif : connatre les principales fonctionnalits offertes par les chanes de caractres :
comparer, rechercher, construire des chanes de caractres, accder aux caractres dune chane.

8.1

Type chane de caractres


Le type chane de caractres permet de reprsenter des squences de caractres de longueur quelconque. Les chanes de caractres servent notamment constituer des textes pour communiquer
avec les utilisateurs des programmes.
En Java, les chanes de caractres sont ralises par le type String . Chaque chane de caractres
est un objet. Ceci implique notamment que les chanes sont toujours dsignes par rfrence.
Une notation de valeur de chane se note entre doubles apostrophes :
"La Cigale et la Fourmi"
Une telle notation provoque la cration, en mmoire, dun objet qui vaut cette chane et elle rend en
rsultat la rfrence cet objet (ladresse de cet objet).

"La Cigale et la Fourmi"


espace des objets

Par ailleurs, une dclaration de variable de type chane scrit :


String titreDeFable;
On peut alors affecter une telle variable la rfrence un objet chane de caractres, par exemple :
titreDeFable = "La Cigale et la Fourmi";

titreDeFable
"La Cigale et la Fourmi"
espace des objets

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

133

Type chane de caractres

Le fait que les chanes soient des objets, donc dsigns par rfrence, oblige prendre certaines prcautions pour comparer des chanes. Loprateur de comparaison == compare les rfrences et
non les valeurs. Dans lexemple suivant, titreDeFable et ch2 dsignent des chanes de mme
valeur "La Cigale
et la Fourmi"
. Cependant titreDeFable==ch2 est faux, car
titreDeFable et ch2 dsignent des objets diffrents. Pour comparer la valeur de deux chanes,
Java fournit la fonction .equals() . Ici titreDeFable.equals(ch2) rend vrai.
Ce point est une source frquente derreur, car titreDeFable==ch2 nest pas une faute de
syntaxe, cest officiellement la comparaison des rfrences (qui est utile, mais dont lutilit est
extrmement rare dans le cas de chanes de caractres1).
titreDeFable
ch1

"La Cigale et la Fourmi"


"La Cigale et la Fourmi"

ch2

titreDeFable==ch2 est faux


titreDeFable==ch1 est vrai
titreDeFable.equals(ch2) est vrai

espace des objets

Les chane de caractres de type String sont des objets non modifiables. Une fois cre, un objet
de type String aura toujours la mme valeur. Ceci nempche pas, bien videmment, davoir des
variables de type String , mais ce qui varie cest leur valeur qui est une rfrence. Lorsquon
affect un identificateur de type String , on lui fait simplement dsigner une autre chane :
String titre ="la Cigale et la Fourmi";
titre

"La Cigale et la Fourmi"


espace des objets

titre ="les misrables";


titre

"La Cigale et la Fourmi"


"les misrables"
espace des objets

1. Ce qui est critiquable ici ce nest pas la notion de rfrence qui est insparable de la notion dobjet, mais cest le choix
que les chanes de type String soient des objets, alors que de toute vidence on voudrait que ce soient des valeurs.

134

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Chanes de caractres

8.2

Quelques oprations disponibles sur les chanes de caractres


Chane vide :
La chane vide est la chane de longueur nulle. Elle ne contient aucun caractre. Elle se note "".
Concatnation :
s1 + s2 : concatnation de s1 et s2.
Par exemple "La Cigale
" + "et la Fourmi"
rend en rsultat un nouvel objet chane
dont la valeur est "La Cigale
et la Fourmi"
(plus exactement, cre un nouvel objet chane
et rend en rsultat la rfrence cet objet).
Autre exemple : avec titreDeFable dsignant la chane "La Cigale et la Fourmi" :
titreDeFable + " est une fable de La Fontaine"
dsigne une chane de valeur :
"La Cigale et la Fourmi est une fable de La Fontaine"

Longueur :
s.length() : nombre de caractres de la chane s. Cest une valeur entire de type int.
Par exemple "coucou".length() vaut 6, "".length() vaut 0,
et avec String ch="coucou"; ch.length() vaut 6.

Le ime caractre :
s.charAt( i) : rend en rsultat la valeur du ime caractre de s. Les caractres sont numrots
partir de 0 en commenant par la gauche. i est une expression entire dont la valeur doit tre comprise entre 0 et s.length()-1 (bornes incluses).
Exemples :
"La Cigale et la Fourmi".charAt(0) vaut 'L'
"La Cigale et la Fourmi".charAt(4) vaut 'i'
avec String ch="coucou"; ch.charAt(2) vaut 'u'

Comparaison selon lordre alphabtique :


s1.compareTo( s2) : rend en rsultat un entier <0 si s1 est avant s2 par ordre alphabtique, 0 si
s1 est gale s2 et >0 si s1 est aprs s2 par ordre alphabtique.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

135

Quelques oprations disponibles sur les chanes de caractres

Sous-chane
s.substing( i,j) : rend en rsultat la sous-chane de s comprise entre lindice i (compris) et
lindice j (exclu). j doit tre suprieur ou gal i.
Exemple :
"bonjour".substring(2,6) vaut "njou"
"bonjour".substring(5,5) vaut "" (chane vide)
Position dun caractre dans une chane
s.indexOf( c) : rend en rsultat si c apparat dans s, lindice de sa premire occurrence, et rend 1 si c napparat pas dans s.
Exemples :
"bonjour".indexOf('j') vaut 3
"bonjour".indexOf('d') vaut -1
Position dune sous-chane dans une chane
s1.indexOf( s2) : rend en rsultat si s2 apparat dans s1, lindice de sa premire occurrence, et
rend -1 si s2 napparat pas dans s1.
s1.indexOf( s2,i) : rend en rsultat si s2 apparat dans s1 partir de la position i, lindice de sa
premire occurrence partir de la position i, et rend -1 si s2 napparat pas dans s1 partir de la
position i.
Exemples :
"bonjour".indexOf("jo") vaut 3
"bonjour bonsoir".indexOf("bon") vaut 0 (position du "bon" de "bonjour" )
"bonjour bonsoir".indexOf("bon",2) vaut 8 (position du "bon" de "bonsoir" )
Conversions en chanes de caractres
Les chanes de caractres servent essentiellement communiquer en clair de linformation avec
des utilisateurs humains. Cest pourquoi tous les types de base possdent une conversion standard
en chane de caractres. Par exemple, la conversion de lentier 12 est "12", celle du rel 3.14 est
"3.14" , celle du boolen false est "false" et celle du caractre 'z' est "z".
Loprateur de concatnation, not +, admet comme oprande nimporte quel type de base, condition quun de ses deux arguments soit explicitement une chane. Dans ce cas, le compilateur ralise implicitement la conversion standard de cet oprande en chane avant de faire la concatnation.
Exemples :
"toto a " + 12 + " ans"
signifie la mme chose que "toto
cest--dire "toto a 12 ans" .

a " + "12"

+ " ans"

Ceci est trs utilis pour afficher joliment un rsultat. Par exemple, ageDeToto tant un entier,
plutt que de programmer :
136

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Chanes de caractres

System.out.print("toto a ");
System.out.print(ageDeToto);
System.out.println(" ans");
Il est plus concis de programmer :
System.out.println("toto a " + ageDeToto + " ans");

8.3

Exemple de manipulations de chanes de caractres


Nous prendrons comme exemple la mise en lettres majuscules dun texte compos de caractres
quelconques. Il sagit de fabriquer un nouveau texte similaire, mais o toutes les lettres ont t remplaces par la lettre majuscule correspondante. Exemple :
La mise en lettres majuscules de :
"les 24 heures du Mans"
est :
"LES 24 HEURES DU MANS"
Principe :
Nous allons rdiger une fonction
static String majuscule(String txt)
dont le rsultat est le texte rsultant du remplacement dans txt des lettres minuscules par les lettres
majuscules correspondantes.
Le rsultat est une chane de caractre nouvelle, quil faut donc crer dans le corps de cette fonction. On la construit caractre par caractre, laide de la concatnation. Ce rsultat, resul , est
initialis la chane vide et pour chaque caractre de txt on lui concatne le caractre majuscule
correspondant.
Ceci fait natre le besoin dune fonction auxiliaire :
static char majuscule(char c)
qui rend en rsultat la majuscule correspondant au caractre c. Nous convenons que le rsultat de
cette fonction est c lui mme si c nest pas une lettre minuscule.
Pour la transformation minuscule/majuscule, une technique efficace et pratique consiste utiliser
deux chanes de caractres :
la chane minuscule : "abcdefghijklmnopqrstuvwxyz"
et la chane majuscule , qui contient en chaque position k la majuscule correspondant la minuscule de position k : "AABCCDEEEEFGHIIJKLMNOOPQRSTUUUUVWXYZ"
Par soucis de gnralit nous avons prvu les lettres accentues (du Franais) ainsi que le . Pour
simplifier, avons dcid que la majuscule correspondante serait sans accent ni cdille :

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

137

Exemple de manipulations de chanes de caractres

class TestTexteMajuscule{
static final String
minuscules = "abcdefghijklmnopqrstuvwxyz";
static final String
majuscules = "AABCCDEEEEFGHIIJKLMNOOPQRSTUUUUVWXYZ";
static char majuscule(char c){
// rsultat
: la majuscule correspondant c
// (le rsultat est c si c nest pas une lettre minuscule)
int k=minuscules.indexOf(c);
if(k==-1){return c;}
else{return majuscules.charAt(k);}
}
static String texteMajuscule(String txt){
// rsultat
: texte rsultant de txt par remplacement de toute
// lettre par la majuscule correspondante
String resul="";
for(int i=0;i<txt.length(); i++){
resul=resul+majuscule(txt.charAt(i));
}
return resul;
}

138

public static void main(String[] z){


System.out.println(texteMajuscule("les 24 heures du Mans"));
}

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Chanes de caractres

QCM 8.1
On considre les dclaration et crations suivantes de chanes de caractres :
String
String
String
String
String

s1
s2
s3
s4
s5

=
=
=
=
=

"tant va la";
"cruche leau";
s1+s2;
s1+" "+s2;
s1+""+s2;

1 - Lexpression s3=="tant va la cruche leau" vaut true.


2 - Lexpression s4=="tant va la cruche leau" vaut true.
3 - Lexpression s5=="tant va la cruche leau" vaut true.
4 - Lexpression s3.equals("tant va la cruche leau") vaut true.
5 - Lexpression s4.equals("tant va la cruche leau") vaut true.
6 - Lexpression s5.equals("tant va la cruche leau") vaut true.
7 - Lexpression s1.substring(0,3)=="tan" vaut true.
8 - Lexpression s1.substring(0,3)=="tant" vaut true.
9 - Lexpression s1.substring(0,3).equals("tan") vaut true.
10 - Lexpression s1.substring(0,3).equals("tant") vaut true.
QCM 8.2
Soit la dclaration et cration de chane :
String s = "abcd";

1 - La longueur de s est donne par lexpression s.length .


2 - La longueur de s est donne par lexpression s.length() .
3 - La longueur de s est donne par lexpression length(s) .
4 - Lexpression s.charAt(2) vaut le caractre 'b'.
5 - Lexpression s.charAt(2) vaut le caractre 'c'.
6 - Lobjet chane de caractre dsign par s est modifiable.
7 - Linstruction s.charAt(2)='x'; transforme la chane dsigne par s en "axcd" .
8 - Linstruction s.charAt(2)='x'; transforme la chane dsigne par s en "abxd" .
9 - s.charAt(2)='x'; est une erreur de syntaxe.
10 - Linstruction s=s.substring(0,2)+'x'+s.substring(3,4); cre la chane

"axcd" et la fait dsigner par s.

11 - Linstruction s=s.substring(0,2)+'x'+s.substring(3,4);
"abxd" et la fait dsigner par s.
12 - s=s.substring(0,2)+'x'+s.substring(3,4);
13 - s=s.substring(0,2)+'x'+s.substring(3,4);

tion.

Module A.P.I. Algorithmique Programmation Imprative

cre la chane

est une erreur de syntaxe.


provoque une erreur lexcu-

Universit de Rennes 1

139

Exemple de manipulations de chanes de caractres

Exercice 8.1

Comptage dun caractre dans une chane

objectif : savoir parcourir une chane de caractres et accder ses caractres.


Rdiger et tester la fonction :
static

int nombreDe(char c,

String

s)

qui rend en rsultat le nombre doccurrences du caractre c dans la chane s.


Exemples :
nombreDe('a',"toto") = 0
nombreDe('o',"toto") = 2

Exercice 8.2

Recherche dune sous-chane

objectif : savoir comparer les caractres de deux chanes, inventer une fonction auxiliaire, se
mfier des dpassement dindices au sein dune chane.
Sans utiliser la fonction indexOf , rdiger et tester la fonction :
static

int indiceDeSousChaine(String

s1, String

s2)

qui, si s1 est une sous-chane de s2, rend en rsultat lindice de la premire occurrence de s1 dans
s2, sinon -1.
Exemples :
indiceDeSousChaine("toto","0+0 = la tte toto") = 16
indiceDeSousChaine("zozo","0+0 = la tte toto") = -1
indiceDeSousChaine("0 =","0+0 = la tte toto") = 2
indiceDeSousChaine("","0+0 = la tte toto") = 0
Remarque : penser rdiger une fonction auxiliaire.
Exercice 8.3

Interprtation dune chane de chiffres dcimaux en un entier

objectif : savoir utiliser une chane auxiliaire comme rservoir dinformation (ici, la suite des
chiffres "0123456789")
Le but de cet exercice est de raliser la conversion traditionnelle dune chane caractres compose
uniquement de chiffre dcimaux en lentier reprsent en dcimal par cette chane. rdiger et tester
la fonction :
static int interpretationDecimale(String s)
// prrequis
: s ne contient que des chiffres dcimaux
// et linterprtation dcimale de s est < 2**31
// rsultat
: linterprtation dcimale de s
Le prrequis interprtation dcimale de s < 231 sert garantir que le rsultat soit reprsentable
sur un int.
Exemple :
interpretationDecimale("1703") vaut 1703
On conviendra que linterprtation dcimale de la chane vide (aucun chiffres) vaut 0 (convention
raisonnable et pratique).
140

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Chanes de caractres

Aide 8.1

Comptage dun caractre dans une chane

Il suffit de parcourir la chane de caractre, au moyen dune boucle


for (int i;i<s.length;i++)
pour incrmenter un compteur (resul ) chaque fois que le caractre en position i est gal c.

Aide 8.2

Recherche dune sous-chane

Lide gnrale consiste chercher pour chaque position i de s2, la chane s1 est prsente partir
de cette position. Cela fait apparatre le besoin dune fonction auxiliaire :
static boolean occurrence(int i, String s1, String s2)
qui indique si s1 est prsente en position i dans s2.
Cette fonction compare les caractres de s1 aux caractres de s2 partir de la position i. Ceci est
ralis par une itration qui utilise k comme indice de caractre dans s1 et i+k comme indice de
caractre dans s2. La boucle sarrte la premire diffrence rencontre. En sortie de boucle, s1
est dans s2 si et seulement si tous les caractres de s1 ont t compars avec succs, cest--dire si
k est gal la longueur de s1.
Attention : un indice de caractre dans une chane doit tre positif ou nul et strictement infrieurs
la longueur de la chane. Se mfier des cas o partir de la position i les caractres de s2 sont en
nombre infrieur la taille de s1 :
k
i
s1

s2

Aide 8.3

Interprtation dune chane de chiffres dcimaux en un entier

Linterprtation dcimale de la chane de chiffres cncn-1...c1c0 est :


[cn].10n+[cn-1].10n-1+...[c1].10+[c0]
dans cette formulation, [cn] dnote la valeur numrique attribue au chiffre c. Cela fait apparatre le
besoin dobtenir la valeur numrique dun chiffre dcimal. Nous rdigeons donc une fonction
auxiliaire :
static int valeurDecimale(char c){
// prrequis
: c est un chiffre dcimal
// rsultat
: la valeur numrique de c
Un moyen simple de raliser cette fonction est dutiliser lindice du chiffre dans la chane
"0123456789" . Par exemple lindice de '3' dans cette chane est lentier 3, justement ce que
lon dsire.
Pour valuer la somme [cn].10n+[cn-1].10n-1+...[c1].10+[c0], on peut utiliser les puissances de 10.
On peut galement valuer cette formule sous la forme :
(...([cn].10+[cn-1]).10+...[c1]).10+[c0]
(Cette forme dvaluation dun polynme est connue sous lappellation schma de Horner).

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

141

Exemple de manipulations de chanes de caractres

142

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

Tableaux

CHAPITRE 9

objectif : savoir utiliser les tableaux pour organiser des collections de donnes

9.1

Intrt des tableaux


Un tableau regroupe une collection de donnes de mme type, chaque lment tant dsign
laide dun indice . Les indices sont nombres entiers dun intervalle.
La figure suivant illustre un tableau de 5 nombres rels. Les lments sont indics de 0 4.
T:

0
1
2
3
4

3.97
1.23
0.56
1.23
7.90

Si le tableau sappelle T, lexpression T[2] dsigne la donne dindice 2 qui vaut 0.56.
Lusage dun tableau est justifi dans les cas suivants :

Lorsque la quantit de donnes est important : il nest pas question de rdiger un programme qui
dfinisse autant de noms individuels pour manipuler ces donnes.

Lorsquon doit dterminer par calcul quels lments de la collection sont utiliss.

Exemple 1
Un morceau de musique numrique est constitu dchantillons damplitude du signal sonore. Il
faut environ 20 000 chantillons par seconde denregistrement. Chaque chantillon tant un nombre
entier, il faut 2 400 000 valeurs entires pour reprsenter 2 minutes de musique. Pour appliquer certains traitements un tel enregistrement, on pourra le conserver dans un tableau :
tableau dentiers [0...2 399 999] morceauDeMusique
Ce tableau est tel que morceauDeMusique[t] dsigne lchantillon de signal sonore mesur
lintervalle de temps numro t.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

143

Cration et nommage des tableaux en Java

Exemple 2
Si on a frquemment besoin dobtenir le nom dun mois partir dun numro de mois, on pourra
regrouper les noms de mois, des chanes de caractres, dans un tableau de 12 lments indic de 1
12.
tableau de chanes de caractres [1...12] nomDuMois
Avec un tel tableau, nomDuMois[3] dsignera le nom du 3me mois, et vaudra sans doute mars.
Lexemple 1 est typique de lusage dun tableau pour une grosse quantit de donnes. Mais la fonction de correspondance t morceauDeMusique[t] est galement importante pour les traitements
sur le son. Dans lexemple 2 la collection de valeurs est relativement petite. Le tableau est utilis
pour tablir une correspondance entre entiers et lments, n nomDuMois[n].

9.2

Cration et nommage des tableaux en Java


objectif : savoir crer et dclarer des tableaux en Java. Comprendre les particularits des
tableaux en Java : ils sont toujours cre par new et dsigns par rfrence.
En Java les tableaux sont obligatoirement crs par new et dsigns par rfrence (ce nest pas le
cas dans dautres langages).
new TypeDesElments[taille]
cre un tableau de taille indique par taille dont les lments sont de type TypeDesElments. Cette
expression rend en rsultat la rfrence au tableau cr.
Exemples :

new int[240000] tableau de 240 000 entiers


new String[12] tableau de 12 chanes de caractres
0
1
2

0
1
2
3
4
5
6
7
8
9
10
11

239999

Par ailleurs, on peut dclarer des variables, constantes, paramtres ou rsultats de fonctions de type
tableau, cest--dire de type rfrence un tableau.
La forme gnrale dune dclaration de variable rfrence tableau est :
TypeDesElments[] nomDeTableau;
Exemples :
int[] morceauDeMusique; rfrence un tableau dentiers appel morceauDeMusique
String[] nomDuMois; rfrence un tableau de chanes de caractres appel nomDuMois
La dclaration de tableau nindique pas la taille du tableau. Cest normal car la taille est dcide au
moment de la cration du tableau et figure donc dans lexpression de cration new
nomDeType[taille]. La dclaration seule ne cre pas de tableau. Dans les exemples ci-dessus, les
144

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

variables morceauDeMusique et nomDuMois sont initialises la rfrence null qui ne dsigne rien. Le captage dans une variable de la rfrence un tableau se fait par une affectation :
morceauDeMusique = new int[240000];
nomDuMois = new String[12];
morceauDeMusique

0
1
2

239999

???
???
???
???
???
???

nomDuMois
0
1
2
3
4
5
6
7
8
9
10
11

???
???

null
null
null
null
null
null
null
null
null
null
null
null

Quand cest possible, on a intrt regrouper cration et dclaration :


int[] morceauDeMusique = new int[240000];
String[] nomDuMois = new String[12];
Les lments dun tableau en Java sont des variables du type indiqu pour les lments : int,
char, double , boolean ... pour les types de base et rfrence pour les objets de type classe,
chanes de caractres et tableaux. Lorsque le tableau est cr, les valeurs des lments ne sont pas
initialises dans le cas dlments dun type de base, et sont initialises null dans le cas dlments de type rfrence.
La taille dun tableau peut tre obtenue au moyen de lattribut length : la taille du tableau T
sobtient par lexpression T.length .
Remarque : la taille dun tableau peut tre gale 0, et cela est utile dans certains cas particuliers.

9.3

Accs aux lments de tableau


En Java, les tableaux sont toujours indics partir de 0. Llment i dun tableau tab se note :
tab[i]
i est une expression entire dont la valeur doit tre comprise entre 0 et taille-1 (bornes incluses). Si
au moment de lexcution i nest pas dans cet intervalle, cela provoque une erreur accs hors des
bornes dun tableau (Array Out Of Bound Exception).
Lindiciation partir de 0 est rarement gnante. Cest par exemple naturel pour lexemple morceauDeMusique , car on a coutume de prendre lorigine des temps 0, donc morceauDeMusique[t] reprsente bien lchantillon au temps t. Cest moins naturel pour lexemple
nomDuMois car on a lhabitude de numroter les mois partir de 1. Dans ce cas il faut appliquer
une correction. Pour dsigner le nom du kime mois il faut utiliser lexpression :
nomDuMois[ k-1] nom du kime mois pour k [1...12]
Un lment de tableau, tab[i] se comporte comme une variable. Il peut donc tre affect :
tab[i] = expression;

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

145

Consquences de la dsignation par rfrence

Par exemple voici les affectations qui initialisent le tableau des noms de mois :
nomDuMois[0] = "janvier";
nomDuMois[1] = "fvrier";
nomDuMois[2] = "mars";
...
nomDuMois[11] = "dcembre";
Pour un tableau de taille importante comme le morceau de musique, linitialisation se fait gnralement au moyen dune boucle qui parcourt tous les lments au moyen dune variable entire. Le
tableau pourra tre initialis avec des valeurs lues dans un fichier :
for (int i=0; i<240000; i++) {
morceauDeMusique[i] = fichierDeMusique.lireUnEntier();
}
Dans cet exemple, la fonction fichierDeMusique.lireUnEntier() est suppose lire
chaque appel une nouvelle valeur entire depuis le fichier fichierDeMusique .
Aprs ces initialisations, les tableaux contiennent enfin des valeurs exploitables :
morceauDeMusique

0
1
2

239999

9.4

12
7
2
-1
-12
-8

nomDuMois

45
43

0
1
2
3
4
5
6
7
8
9
10
11

"janvier"
"fvrier"
"mars"
"avril"
"mai"
"juin"
"juillet"
"aot"
"septembre"
"octobre"
"novembre"
"dcembre"

Consquences de la dsignation par rfrence


Un consquence importante de la dsignation des tableaux par rfrence est la suivante :
Lorsquune fonction admet un tableau en paramtre, cest la rfrence au tableau paramtre effectif
qui est transmise lors dun appel (et non une copie de ce tableau). Si la fonction modifie des lment
du tableau, cest le tableau pass en paramtre qui est modifi. Ceci permet donc de programmer
des procdures qui transforment des tableau.
Exemple :
Considrons la procdure suivante, metAZero , qui met zro llment dindice k dun tableau
de nombres rels.
static void metAZero(double[] T, int k){
// prrequis
: k>=0 et k<T.length
// effet
: met zro T[k]
T[k]=0;
}

146

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

Si on lutilise ainsi :
double[] T1 = {4,12,67,1};
metAZero(T1,2);
T1 se trouve modifi. Il vaut {4,12,0,1}
Lappel a eu un effet. Cest dailleurs ce que dit la spcification de cette procdure.
Si on ne dsire pas deffet, mais obtenir un nouveau tableau partir dun tableau pass en paramtre, la fonction doit crer le nouveau tableau et ne pas modifier celui pass en paramtre. Cest le
cas de la fonction suivante, miseAZero , qui est sans effet et rend en rsultat un nouveau tableau
obtenu partir de T en remplaant par 0 la valeur de llment k.
static double[] miseAZero(double[] T, int k){
// prrequis
: k>=0 et k<T.length
// rsultat
: nouveau tableau ayant zro en position k
// et les mme valeurs que T dans les autres positions
double[] resul = new double[T.length];
for(int i=0; i<T.length; i++){
resul[i]=T[i];
}
resul[k]=0;
return resul;
}
Ici, nous avons cr au sein de la fonction un nouveau tableau resul , de mme taille que T. Puis
nous avons copi T dans resul (on appelle cela cloner le tableau T). Ensuite nous avons mis 0
llment k de resul . Et enfin nous avons rendu resul en rsultat.

9.5

Dclarations de tableaux initialiss


objectif : savoir utiliser des tableaux initialiss comme jeu de test ou comme intermdiaire de
codage.

Java permet, lors de la dclaration dun tableau, de crer le tableau et ses lments. La forme gnrale est :
typeDesElments[] nomDeTableau = {e0, e2... ek};
Exemples :
int[] lesDixPremiersEntiers = {0,1,2,3,4,5,6,7,8,9};
char[]
chiffresDecimaux
= {'0','1','2','3','4','5',6','7','8','9'};

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

147

Dclarations de tableaux initialiss

Ceci est trs utile, notamment en deux circonstances :

Pour fournir des tests reproductibles :


Cest une trs mauvaise ide que de tester des fonctions en introduisant les donnes par des lectures au clavier. Il est prfrable de rdiger des programmes de test qui contiennent toutes les
donnes dans la procdure principale. Ainsi les tests sont facilement reproductibles. Dans les
cas de donnes qui sont des collections, un tableau initialis convient parfaitement. Exemple :
soit tester une fonction max qui rend en rsultat le maximum dun tableau dentiers.
class TestMax {
static int max(int[] T){
// prrequis
: T.length > 0
// (le maximun dun domaine vide nest pas dfini)
// rsultat
: le maximum des lments de T
int resul=T[0];
for (int i=1; i<T.length; i++){
if(T[i]>resul) {resul=T[i];}
}
return resul;
}

public static void main(String[] z){


// programme de test de la fonction max
int[]T1 = {12,56,4,89,-23,0,56};
System.out.println("le max de T1 = " + max(T1));
int[]T2 = {1,6,0,92};
System.out.println("le max de T2 = " + max(T2));
}

Pour raliser facilement des encodages ou dcodages de donnes.


Comme exemple on peut considrer la fonction suivante qui fournit le nom en clair dun mois
partir de son numro :
static String nomDuMois(int n){
// prrequis
: n>=1 et n<=12
// rsultat
: le nom du mois numro n
final String[] mois = {
"janvier","fvrier","mars","avril","mai","juin","juillet",
"aut","septembre","octobre","novembre","dcembre"};
return mois[n-1];
}

148

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

9.6

Exemples dutilisation de tableaux


Exemple 1
Le programme suivant utilise un tableau population pour mmoriser une collection de personnes. Une personne est dcrite par la structure Personne , compose du nom, de ladresse et de
lge de la personne. Le programme utilise une boucle pour initialiser le tableau (population de 5
personnes), puis cherche et affiche les caractristiques dune personne de nom donn. Si aucune
personne de ce nom ne figure dans le tableau, le programme affiche aucune personne de ce nom.
class Personne {
public String nom;
public String adresse;
public int age;
public
Personne(String
sonNom,
String
nom=sonNom; adresse=sonAdresse; age=sonAge;
}
}

sonAdresse,

int

sonAge)

enum Etat {nonDecide,absent,present}


class TestPopulation {
public static void main(String[] arg) {
Personne[] population = new Personne[5];
for (int i=0; i<population.length; i++) {
System.out.println("entrer une personne
:");
System.out.print("nom
: "); String nom=Lecture.chaine();
System.out.print("adresse
: ");
String adresse=Lecture.chaine();
System.out.print("age
: "); int age=Lecture.unEntier();
population[i] = new Personne(nom, adresse, age);
}
System.out.print("nom de la personne cherche
: ");
String nomCherche=Lecture.chaine();

int etatRecherche=Etat.nonDecide;
int i=0;
while (etatRecherche==Etat.nonDecide) {
if (i==population.length) {etatRecherche=Etat.absent;}
else if (population[i].nom.equals(nomCherche)) {
etatRecherche=Etat.present;
}
else {i++;}
}
if (etatRecherche==Etat.absent){
System.out.println("aucune personne de ce nom");
}
else /*etatRecherche==Etat.present*/ {
System.out.println("nom
: " + population[i].nom
+ " adresse
: " + population[i].adresse
+ " ge : " + population[i].age
+ " ans");
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

149

Raisonnement sur les tableaux - notion de tranche

Exemple 2
On a 100 nombres entiers dans un fichier. On dsire rechercher combien de nombres sont infrieurs
ou gaux la moyenne, et combien sont suprieurs. Il nous faut dans un premier temps calculer la
moyenne des nombres, ce qui impose un parcours de toute la suite des nombres. Puis on doit la parcourir nouveau pour compter ceux qui sont infrieurs ou gaux la moyenne. La lecture dun
fichier est relativement longue. Pour viter de lire deux fois le fichier on peut utiliser un tableau qui
mmorise les nombres. Le comptage utilise alors le tableau.
class ComptageDesInferieursALaMoyenne {
public static void main(String[] arg) {
LectureFichierTexte fichierDeNotes =
new LectureFichierTexte("notesDeMath.txt");
final int nombreDeNotes=100;
int[] note = new int[nombreDeNotes];
// initialisation du tableau note
// avec les valeurs lues dans le fichier de notes
// et calcul de la moyenne des notes
double sommeDesNotes=0;
for (int i=0; i<nombreDeNotes; i++) {
note[i] = fichierDeNotes.lireUnEntier();
sommeDesNotes = sommeDesNotes+note[i];
}
double moyenneDesNotes = sommeDesNotes/nombreDeNotes;
// comptage de nombre de notes inf. ou gales la moyenne
int nbNotesInferieuresALaMoyenne=0;
for (int i=0; i<nombreDeNotes; i++) {
if (note[i]<=moyenneDesNotes) {
nbNotesInferieuresALaMoyenne++;
}
}

9.7

System.out.println("moyenne
des : notes
" + moyenneDesNotes);
System.out.println(nbNotesInferieuresALaMoyenne
+ " notes sont infrieures ou gales la moyenne");
System.out.println(
(nombreDeNotes - nbNotesInferieuresALaMoyenne)
+ " notes sont suprieures la moyenne");
}

Raisonnement sur les tableaux - notion de tranche


objectif : savoir raisonner en utilisant la notion de tranche, zone dlments conscutifs dun
tableau.
De nombreux traitements sur des tableaux se rsolvent en utilisant la notion de tranche. Une tranche
est une portion de tableau dlimite par deux indices i et j. La tranche allant de lindice i lindice

150

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

j pourra tre note T[i..j] si la position j est comprise et T[i..j[ si la position j est exclue.
0

i tranche T[i..j] j

T.length

Par convention, T[i..j] est une tranche vide si j<i.


Le principe de rsolution suit gnralement le schma suivant :

La proprit dsire pour le rsultat est trivialement vraie pour une tranche vide, ce quon

obtient, par exemple en posant i=j=0, pour la tranche T[i..j[ .


Cette proprit est maintenue vraie par une itration dont le corps fait strictement crotre la
taille de la tranche.
Litration se termine lorsque la tranche est devenue tout le tableau.
La proprit concernant le tableau est alors assure.
Exemple 1 : indice dun lment maximal dans un tableau
=La fonction suivante rend en rsultat un indice contenant la valeur maximale dun tableau. Lalgorithme consiste conserver dans une variable max le maximum de la tranche T[0..i[ et dans une
variable indiceDuMax lindice de ce maximum. Ceci conduit une itration qui initialise max
T[0], indiceDuMax 0 et fait varier i de 1 T.length en maintenant vrai linvariant :
T[indiceDuMax] est maximal sur la tranche T[0..i[
En sortie ditration, i vaut T.length et donc T[indiceDuMax] est bien maximal sur T.
static int indiceDeMax(double[] T){
// prrequis
: T.length>0
// rsultat
: indice dun lment maximum de T
double max=T[0]; int indiceDuMax=0;
int i=1;
while(i<T.length){
// invariant
: T[indiceDuMax]
est maximal
if(T[i]>max){max=T[i]; indiceDuMax=i;}
i++;
}
return indiceDuMax;
}

sur

la tranche

Exemple 2 : tri dun tableau par placements successifs des lments maximaux
Soit raliser une procdure qui trie un tableau par ordre croissant. Le tri est une opration trs utile
car, une fois tri, la recherche dinformation dans un tableau est considrablement plus rapide que
dans un tableau en vrac. Le tri dun tableau T1 est un tableau T2 qui a les mmes lments que
T1 (en valeur et en quantit pour chaque valeur) mais tel que si i>j alors T2[i]>=T2[j]. Nous
nous intressons ici une procdure de tri, qui a pour effet de trier un tableau pass en paramtre.
Le tri se fera donc sur le tableau lui-mme (par opposition une fonction qui rendrait en rsultat un
nouveau tableau tri).Le principe que nous allons mettre en uvre nest pas trs efficace, mais il a

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

151

T[0

Raisonnement sur les tableaux - notion de tranche

lavantage dtre simple comprendre :


Supposons le tableau partiellement tri dans sa partie suprieure. Plus prcisment, la tranche
T[i..T.length[ est trie et tous ses lments sont suprieurs ceux de la tranche T[0..i[ .
0

iMax

15

15

partie trie

T.length

Si nous cherchons un indice iMax du maximum de la tranche T[0..i[ , en changeant les valeurs
de T[iMax] et de T[i-1] , la partie trie grossit dune unit. Il suffit donc dappliquer ce procd pour i variant de T.length 1.
La recherche de iMax peut se faire au moyen dune fonction similaire la fonction de lexemple
prcdent. La seule diffrence est que cette fonction cherche un indice du maximum sur une tranche
T[0..k[ et non sur le tableau complet. Il faut donc rajouter un paramtre k qui indique la borne
suprieure de la tranche :
static int indiceDeMax(double[] T, int k){
// prrequis
: k>0 et k<=T.length
// rsultat
: indice dun lment maximum de la tranche T[0..k[
double max=T[0]; int indiceDuMax=0;
int i=1;
while(i<k){
// invariant
: T[indiceDuMax]
est maximal
sur la tranche
if(T[i]>max){max=T[i]; indiceDuMax=i;}
i++;
}
return indiceDuMax;
}
Le tri est alors ralis par une itration qui maintient linvariant :
T a les mmes lments que Tinitial (valeur de T lors de lappel), T[i..T.length[ est tri et
les lments de T[i..T.length[ sont suprieurs ou gaux ceux de T[0..i[ .
static void trier(double[] T){
// effet
: tri T par ordre croissant
int i=T.length;
while(i>0){
// invariant
: T a les mmes lments que Tinitial
// T[i..T.length[
est tri et les lments de T[i..T.length[
// sont suprieurs ou gaux ceux de T[0..i[
int iMax=indiceDeMax(T,i);
double vMax=T[iMax]; T[iMax]=T[i-1]; T[i-1]=vMax;
i--;
}
}
En sortie ditration, i vaut 0, et le tableau est donc intgralement tri.
152

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

T[0..

Tableaux

Exemple 3 : recherche dichotomique dans un tableau tri


On se propose de trouver lindice dune valeur dans un tableau tri. Le tableau est suppos tri par
ordre croissant. Si la valeur v cherche se trouve dans le tableau, la fonction recherche doit rendre en rsultat un indice i tel que T[i]==v, sinon elle doit rendre -1 (cest sans ambigut puisque
les indices commencent 0).
Dans cet exemple nous cherchons une valeur relle dans un tableau de nombres rels. Plus gnralement ce principe sapplique la recherche dans un tableau dont le type des lments est dot
dune relation dordre de manire donner un sens la notion dtre tri selon cet ordre. Ce pourrait tre par exemple un tableau de personnes tries selon lordre alphabtique de leurs noms.
Puisque le tableau est tri, on peut pratiquer un algorithme de recherche dichotomique, qui est considrablement plus rapide quune recherche par examen des lments successifs.
Le principe consiste considrer une tanche T[i..j] dans lequel la valeur v est susceptible de se
trouver (on est sr quelle ne peut tre ailleurs).

Initialement i=0 et j=T.length-1 , de sorte quon commence avec le tableau complet.


La recherche est ralise par une itration qui compare v la valeur prsente au milieu m de la

tranche. Si T[m]>v, la tranche suivante est T[i..m-1] et si T[m]<v, la tranche suivante est
T[m+1..j] .
Litration termine soit lorsque T[m]==v, auquel cas m est lindice cherch, soit lorsque i>j,
auquel cas v nest pas dans le tableau puisque la tranche o il pourrait de trouver est devenue
vide.
Linvariant de cette itration est :
v nest pas prsent en dehors de la tranche T[i..j]
static int recherche(double[] T, double v){
// prrequis
: T est tri par ordre croissant
// rsultat
: un indice i tel que T[i]=v si v est prsent dans T,
// -1 sinon
int i=0; int j=T.length-1;
int milieu=(i+j)/2;
while(i<=j && T[milieu]!=v){
// invariant
: v nest pas prsent
en dehors de la tranche
if(T[milieu]>v){j=milieu-1;}
else / * T[milieu]<v */ {i=milieu+1;}
milieu=(i+j)/2;
}
if(i<=j){return milieu;}
else {return -1;}
}
En sortie ditration, si i<=j, milieu est lindice cherch (puisque litration sest termine avec
T[milieu]==v ), sinon v nest pas prsent dans T, puisque lintervalle o il est susceptible de se
trouver est vide.
La terminaison de cette itration est garantie par la dcroissance stricte de la taille de la tranche
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

153

T[i

Tableaux plusieurs dimensions

T[i..j] . Il faut cependant tre prudent. Cette dcroissance est stricte car on considre soit
[i..milieu-1] soit [milieu+1..j] dont les tailles sont strictement infrieures la taille de
[i..j] . Ce nest pas toujours le cas pour [i..milieu] ou [milieu..j] qui ont mme taille
que [i..j] si i=j. Dans ce cas, milieu=i et litration risque de ne pas terminer (cest ce qui
se passe effectivement si on programme ainsi).
Plus formellement, on peut prendre i-j comme fonction de terminaison. Lintervalle est divis par
2 chaque itration, ce qui garantit un temps de recherche infrieur k.log(T.length ). Par exemple, pour un tableau dun million dlments, la recherche ncessite au plus 20 pas ditration, car
2201000000.

9.8

Tableaux plusieurs dimensions


objectif : connatre lexistence des tableaux plusieurs dimensions et leur gnralisation Java
sous la forme de tableaux de tableaux.
Un tableau plusieurs dimensions est un tableau dont les lments sont dsigns par plusieurs indices. Le domaine de chaque indice est un intervalle dentiers. Par exemple un chiquier peut tre
reprsent par un tableau 88, 8 lignes et 8 colonnes, dans lequel chaque lment indique la pice
qui sy trouve (pion blanc, pion noir, tour blanche, tour noire...) ou bien libre sil ne sy trouve
aucune pice.
tableau de PiceDEchec[1...8] [1...8] chiquier
Avec un tel tableau, on accde llment de la ligne i, colonne j par chiquier[i,j].
En Java, un tableau plusieurs dimensions est ralis par un tableau de tableaux, cest--dire un
tableau dont les lments sont des tableaux. Un tel tableau peut tre dclar et cr ainsi :
PieceDEchec[][] echiquier = new PieceDEchec[8][8];
En Java, un tableau tant toujours dsign par rfrence, un tableau de tableaux est donc un tableau
de rfrences des tableaux. Ainsi, lexpression new PieceDEchec[8][8] cre 8 tableaux de
taille 8, les lignes et un tableau de 8 rfrences ces tableaux :
echiquier

0 1 2 3 4 5 6 7

1
2
3
4
5

echiquier[4][6]

6
7

154

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

Accs aux lments :


Ce qui a t dit pour les tableaux une dimension se gnralise facilement. Laccs un lment se
fait en indiquant la valeur dindice pour chaque dimension. Par exemple :
echiquier[4][6]
dsigne la pice qui se trouve la ligne 4 et colonne 6 de lchiquier (se mfier du fait que les indices commencent 0).
En supposant que les pices dchec sont reprsentes par une structure PieceDEchec :
enum Nature {pion,tour,cavalier,fou,reine,roi}
enum Couleur {blanc,noir}
class PieceDEchec {
public Nature nature;
public Couleur couleur;
public PieceDEchec(Nature saNature, Couleur saCouleur){
nature=saNature; couleur=saCouleur;
}
}
Le placement dun cavalier blanc en position (4,6) scrit :
echiquier[4][6] = new PieceDEchec(Nature.cavalier,Couleur.blanc);
Tableaux irrguliers :
Le tableau de tableaux offert par Java est une notion plus gnrale que le tableau plusieurs
dimensions :

Si on ne donne quun seul indice, on obtient un lment qui est lui mme un tableau de dimen-

sion immdiatement infrieure : echiquier[4] dsigne la ligne 4 de lchiquier, et cest la


rfrence un tableau de dimension 1.
Rien ninterdit que les lments du premier tableau soient de tailles diffrentes. Lexemple suivant illustre un tableau annee2001 qui reprsente les 12 mois de lanne 2001. Chaque lment de ce tableau est un tableau indic par le numro de jour du mois, de taille 31, 28 ou 30
selon le mois :
int[][] annee2001= new int[][12)
annee2001[0]=new int[31];
annee2001[1]=new int[28];
...
annee2001[11]=new int[31];
annee2001
janvier
fvrier
mars
avril

0
1
2
3

0 1 2 3 4 5 6 7

...

27 28 29 30

dcembre 11

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

155

Tableaux plusieurs dimensions

Remarques :

Le nombre de lignes dun tableau M deux dimensions sobtient par M.length . Cest nor-

mal puisque M, en tant que tableau (de tableaux) est le tableau des lignes.
Dans la mesure o la ligne i de M existe, son nombre dlment est M[i].length .
Si M est un tableau rectangulaire rgulier et de taille non nulle, son nombre de collonnes
peut sobtenir par M[0].length .
Exemple dutilisation de tableaux rguliers 2 dimensions : produit de matrices
Une des utilisations importantes des tableaux deux dimensions est tout ce qui se rapporte au calcul
matriciel : transformations linaires, rsolutions de systmes dquations linaires...
Lexemple suivant illustre le produit de matrices de nombres rels. La fonction produitMatrice a pour paramtres une matrice np A (n lignes, p colonnes) et une matrice pq B. Elle rend
en rsultat la matrice produit C=AB, de n lignes et q colonnes. Le calcul est ralis selon la formule classique Cij = AikBkj, ce calcul tant ralis au moyen de trois boucles imbriques. Le programme principal calcule et affiche le produit de deux matrices 33 donnes sous forme de tableaux
initialiss.
class TestMatrice {
static double[][] produitMatrice(double[][] A, double[][] B) {
// prrequis
: A est une matrice n x p et B une matrice p x q
// rsultat
: le produit matriciel A X B
final int n=A.length;
final int p = B.length;
final int q = B[0].length;
double[][] C = new double[n][q]; // matrice rsultat
for(int i=0;i<n;i++){
for (int j=0;j<q;j++){
C[i][j]=0;
for (int k=0;k<p;k++) {
C[i][j]= C[i][j]+A[i][k]*B[k][j];
}
}
}
return C;
}

156

public static void main(String[] arg) {


double[][] A = {{5,3,8},{6,9,0},{12,67,3}};
double[][] B = {{2,0,0},{0,2,0},{0,0,2}};
double[][] C = produitMatrice(A,B);
System.out.println("produit AxB
:");
for (int i=0;i<3;i++) {
for (int j=0;j<3;j++) {System.out.print(C[i][j]+" ");}
System.out.println();
}
}

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

QCM 9.1

1 - Les tableaux servent faire correspondre des nombres entiers (les indices) et des donnes de
types quelconques mais identiques (les lments du tableau).
2 - Un tableau remplace avantageusement lusage de variables de mme type, car il est plus concis dutiliser des noms de la forme T[0], T[1], T[2], T[3] ... que des identificateurs divers.
3 - En Java, les tableaux sont des objets toujours dsigns par rfrence.
4 - En Java, on peut crer un tableau de 20 lments (de type int par exemple) par une simple

dclaration de la forme : int[20] T;

5 - En Java, un tableau de 20 lments (de type int par exemple) est ncessairement cr par
une instruction : new int[20];
6 - En Java, une dclaration de la forme int[] T;dfinit T comme tant une rfrence un
tableau dentiers et cette rfrence est initialise null, de sorte que T ne dsigne rien.
7 - En Java, T2 tant dclar int[] T2;et T1 dsignant un tableau dentiers de taille 30,
linstruction daffectation T2=T1; provoque la cration dun tableau de taille 30 dont la rfrence
est capte par T2 et la copie des lments de T1 dans le tableau dsign par T2.
8 - En Java, T2 tant dclar int[] T2;et T1 dsignant un tableau dentiers, linstruction
daffectation T2=T1; le captage par T2 de la rfrence au tableau dsign par T1.
QCM 9.2
Soit la squence dinstructions :
String[] T1 = new String[30];
String[] T2 = T1;
T2[4]="bonjour";

1 - Aprs excution de cette squence, T1[4] vaut null.


2 - Aprs excution de la squence prcdente, T1[4] vaut la chane vide "".
3 - Aprs excution de la squence prcdente, T1[4] vaut "bonjour" .
4 - Aprs excution de la squence prcdente, T1[4] na pas de valeur dfinie.
QCM 9.3
On considre les fonctions suivantes :
static void renverseLOrdre(int[] T){
for(int i=0; i<T.length/2; i++){
int v=T[i]; T[i]=T[T.length-i-1]; T[T.length-i-1]=v;
}
}
static int [] ordreInverse(int[] T){
int[] resul= new int[T.length];
for(int i=0; i<T.length; i++){
resul[i]=T[T.length-i-1];
}
return resul;
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

157

Tableaux plusieurs dimensions

1 - La fonction renverseLOrdre modifie le tableau dsign par le paramtre T.


2 - La fonction renverseLOrdre rend en rsultat un nouveau tableau contenant les mmes

lments que T dans lordre inverse renverse de celui o ils figurent dans T.

3 - La fonction renverseLOrdre renverse lordre des lments de T, cest--dire : change


T[i] chang avec T[T.length- i-1] pour tout i de 0 T.length-1 .
4 - La fonction ordreInverse
5 - La fonction ordreInverse

modifie le tableau dsign par le paramtre T.

rend en rsultat un nouveau tableau contenant les mmes lments que T dans lordre inverse renverse de celui o ils figurent dans T.

6 - La fonction ordreInverse renverse lordre des lments de T, cest--dire : change


T[i] chang avec T[T.length- i-1] pour tout i de 0 T.length-1 .
QCM 9.4

1 - En Java, on peut crer un tableau de taille nulle, par exemple un tableau dentiers de zro lments, par new int[0] .
2 - Un tableau de taille nulle est dsign par null.
Considrons un tableau de tableau ainsi dclar : double[][]

T; .

3 - La taille des toutes les lignes de T sobtient par T.length .


4 - La taille de toutes les lignes de T sobtient par T[0].length .
5 - Si T est un tableau rgulier rectangulaire et si T.length!=0,

de T sobtient par T[0].length .

6 - Si i>=0 et i<T.length,

158

Module A.P.I

la taille de toutes les lignes

la taille de la ligne i de T sobtient par T[i].length .

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

Exercice 9.1

Diverses fonctions sur tableaux de chanes de caractres

objectif : ces exercices ont pour but de se familiariser avec lusage des tableaux. La fonction
enClair demand dans lexercice 1 permet de visualiser un tableau. Elle sera trs utile par la
suite pour des tests.
Rdiger et tester les fonctions suivantes de manipulation de tableaux de chanes de caractres :
1 - Fonction enClair telle que enClair(T) rend en rsultat la chane de caractres "en clair"
constitue des lments de T par ordre dindices spars par des blancs. Exemple :

avec :

rsultat :

"toto"
"alfred"
"jerome"
"simon"
"{toto,alfred,jerome,simon}"

2 - Fonction estPresent telle que estPresent(T,s) indique si la chane de caractre s est


prsente dans T. Avec lexemple prcdent :
estPresent(T,"jerome") rend true
estPresent(T,"jules") rend false

3 - Fonction indiceDuplusPetitAlphabetique telle que indiceDuPlusPetitAlphabetique(T,i) soit lindice de la plus petite chane, selon lordre alphabtique, contenu
dans le tableau T partir de lindice i. Pour tester lordre alphabtique on utilisera la mthode
s1.compareTo( s2) qui rend un entier ngatif si la chane s1 est infrieure s2, 0 si les chanes
sont gales et un entier positif si s1 est suprieur s2.
Exemple :

avec :

0
1
2
3

"toto"
"alfred"
"jerome"
"simon"

rsultat : 1 (indice dans T de "alfred" )

4 - Procdure trier telle que trier(T) transforme le tableau T en un tableau contenant tous les
lments que possdait T classs par ordre croissant.
Remarque : on peut crer un tableau initialis au moyen dune instruction de la forme :
String[] T={"toto","alfred","jerome","simon"};
Ceci est trs pratique pour faire des tests reproductibles.
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

159

Tableaux plusieurs dimensions

Exercice 9.2

Tri dun tableau par dtermination des rangs des lments

objectif : les exercices 9.2 9.6 concernent le tri de tableaux. Lobjectif de ces exercice est
double :
- connatre quelques algorithmes classiques de tri,
- apprendre raisonner sur des tranches de tableau.
1 - Rdiger la fonction
static

int nombreDe(double v,

double[]

T)

qui rend en rsultat le nombre doccurrences de v dans T.


2 - Rdiger la fonction
static

int rangDe(double v,

double[]

T)

qui rend en rsultat le rang par ordre croissant de v dans T. Le rang de v dans T est le nombre dlments de T strictement infrieurs v. Ainsi si aucun lment nest infrieur v, le rsultat est 0, et
si tous les lments sont infrieurs v, le rsultat est T.length .
3 - Utiliser les fonctions prcdentes pour programmer la fonction de tri :
static double[] tri(double[] T){
// rsultat
: un nouveau tableau contenant les lments de T
// tris par ordre croissant
Remarque : le principe de ce tri consiste, pour chaque lment de T placer dans le rsultat partir
du rang de cet lment autant de fois cet lment quil a doccurrences dans T. Ce principe simple
conduit replacer plusieurs fois, sa place, la mme valeur dans le tableau rsultat, mais ce nest
pas grave (lviter serait assez compliqu).

Exercice 9.3

Tri par permutations et tri bulle (Bubble sort)

1 - Tri par permutations


Le tri par permutations consiste parcourir le tableau en changeant les lments sils ne sont pas
dans lordre souhait. A chaque parcours, un lment de plus se trouve sa place
partie
trie

3
6
7
12
9
24
56
21
87
33

3
6
7
12
9
24
56
21
33
87

3
6
7
12
9
24
21
56
33
87

3
6
7
12
9
21
24
56
33
87

3
6
7
9
12
21
24
56
33
87

partie
trie

Ainsi, comme le suggre lexemple ci-dessus, chaque parcours la partie trie du tableau saccrot
dau moins un emplacement.
160

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

Pour un tableau de taille n, suffit de rpter n fois ce processus pour trier compltement le tableau.
Puisque chaque parcours la partie trie saccrot dau moins une unit, on vite des comparaisons
inutiles en effectuant le parcours suivant sur une tranche diminue dune unit, cest dire uniquement sur la partie possiblement non trie.
Cet algorithme est souvent prsent sous forme dune mtaphore, la monte de bulles. Considrons le cas dun tri par ordre croissant, comme le montre lexemple prcdent. Un lments T[i]
tel que T[i]<T[i-1] est appel bulle, par analogie avec une bulle de gaz dans un liquide. Une
telle bulle est destine monter vers la surface (les indices faibles ici), par change avec llment prcdent.
Il est bon de concrtiser ce concept par une procdure monteeDeBulles qui ralise une remonte de bulles sur une tranche T[k..T.length[ . Voici sa spcification :
static void monteeDeBulles(double[] T, int k)
// prrequis
: 0<=k<T.length
// effet
: ralise une monte de bulles
// sur la tranche T[k..T.length[
1.1 - Rdiger la procdure monteeDeBulles .
La procdure monteeDeBulles est destine tre utilise dans la procdure de tri, au moyen
dun itration qui procde un succession de remontes de bulles sur des tranches
T[k..T.length[ de tailles dcroissantes de sorte trier totalement le tableau T.
1.2 - Rdiger cette procdure trier.

2 - Tri bulle
Le tri bulle est une amlioration du tri par permutations. Il consiste terminer la procdure de tri
ds que la tentative de monte de bulles ne fait monter aucune bulle (ne procde aucun change).
En effet, cela signifie que la tranche sue laquelle on a tent une monte de bulle est correctement
ordonne, et puisque tous ses lments sont suprieurs la partie dj trie, le tableau est totalement
tri.
Cela ncessite de modifier la procdure de monte de bulles de manire ce quelle indique si une
bulle a effectivement t monte. Cette nouvelle procdure, tenteMonteeDeBulles est ainsi
spcifie :
static boolean tenteMonteeDeBulles(double[] T, int k)
// prrequis
: 0<=k<T.length
// effet
: ralise une monte de bulles
// sur la tranche T[k..T.length[
// rsultat
: indique si une bulle a effectivement t monte
2.1 - Rdiger la procdure tenteMonteeDeBulles .
2.2 - Rdiger cette procdure trier qui ralise un tri bulle.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

161

Tableaux plusieurs dimensions

Exercice 9.4

Tri par comptage (Distribution counting)

Le tri par comptage (Distribution counting) ne peut sappliquer que si le domaine des valeurs est
numrable (un intervalle dentiers par exemple) et pas trop grand. On procde en deux temps :
1 - on compte le nombre doccurrences de chaque valeur prsente dans le tableau trier, dans un
tableau dont la taille est la dimension du domaine possible pour les valeurs,
2 - ensuite on construit le tableau tri en parcourant le tableau des occurrences.
exemple, soit le tableau T :
5

Le tableau des occurrences est :


0

cest--dire : T contient un 2, deux 4, deux 5 et un 7.


Le tableau T tri est alors :
2

obtenu par placements successifs de un 2, deux 4, deux 5 et un 7.


Rdiger la procdure triParComptage qui ralise cet algorithme.
Remarque : la complexit dpend du domaine de valeurs. Cest O(n)+O(m) m tant la taille de
lintervalle des valeurs. Cest intressant si m est petit par rapport n.

Exercice 9.5

Recherche dichotomique, version rcursive

Nous rappelons le principe dune recherche dichotomique dans un tableau tri :


Pour chercher une valeur v dans une portion du tableau comprise entre les indices i et j, on prend
lindice du milieu, (i+j)/2. Si llment cherch sy trouve, cest termin. Si llment cherch est
plus petit que llment du milieu on cherche dans la moiti infrieure. Sil est plus grand on cherche dans la moiti suprieure. Llment ne se trouve pas dans le tableau si lintervalle de recherche
devient vide.
Nous avons vu une ralisation itrative de cette fonction. Rdiger une ralisation rcursive de cette
fonction :
static int indiceDe(int v, int [] T)
// prrequis
: T est tri par ordre croissant
// rsultat
: indice dans T o se trouve
// la valeur v si elle sy trouve,
// -1 si v est absente.
Guide : il faut rdiger une fonction auxiliaire (qui est rcursive) qui cherche v dans une tranche
T[i..j] .

162

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

Exercice 9.6

Tri par fusion

Un algorithme rapide pour trier un tableau consiste :

couper le tableau en deux parties gales ( un prs),


trier les deux parties,
fusionner les deux parties tries T1 et T2 en respectant lordre des lments, cest--dire en prenant chaque fois le plus petit lment parmi les lments non encore pris de T1 ou de T2.
4
12
7
4
19
67
5
67
23
56
12
6

67

23

4
12

4
19

5
67

56
12

12

12

19

67

56

4
7
12
4
19
67
5
23
67
6
12
56

4
4
7
12
19
67

4
4
5
6
7
12
12
19
23
56
67
67

5
6
12
23
56
67

Remarque : la complexit est O(n.log(n)). Cest le mieux que lon puisse faire dans le cas gnral,
pour un tableau nayant aucune proprit particulire.
On veut rdiger selon ce principe une fonction de tri dun tableau dentiers :
static int[] tri(int[] T)
// rsultat
: un nouveau tableau contenant
// les lments de T tris par ordre croissant
Lalgorithme suggr est naturellement rcursif. Son petit dfaut est de ncessiter des tableaux
intermdiaires. Pour viter de crer de nouveaux tableaux lors de lclatement dun tableau en deux
parties, on peut passer en paramtre une tranche du tableau originel, cest--dire le tableau T et
deux indices i et j qui dlimitent les lments trier. Pour cela il faut rdiger une fonction
auxiliaire :
static int[] triTranche(int[] T, int i, int j)
// prrequis
: ???
// rsultat
: un nouveau tableau contenant
// les lments de la tranche T[i..j[

tris par ordre croissant

exprimer tri en utilisant triTranche (trs simple),


indiquer le prrequis raisonnable de triTranche (relation entre i, j et T.length ),
rdiger triTranche (fonction rcursive).

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

163

Tableaux plusieurs dimensions

Exercice 9.7

Manipulation de matrices

Rdiger les fonctions suivantes de manipulation de matrices :


1 - Visualisation en clair : rdiger une fonction enClair qui rend en rsultat une chane de caractres qui visualise en clair un tableau dentiers M deux dimensions. Cette chane comportera un
passage la ligne avant chaque ligne et un espace entre les lments dune ligne.
Exemple : pour
int[][] M = {{6,1,8,5}, {7,5}, {2,9,6}}
limpression du rsultat de enClair(M) donne :
6 1 8 5
7 5
2 9 3
Spcification :
static String enClair(int[][] M){
// rsultat
: chane visualisant M en clair,
// passage la ligne pour chaque ligne,
// un espace entre lments dune ligne
2 - Matrice unit : la fonction matriceUnite doit rendre en rsultat un tableau a deux dimension carr de taille n qui reprsente la matrice unit : des 1 sur la diagonale principale et des 0
ailleurs. Les lments seront des nombres entiers de type int.
Exemple : matriceUnite(4)
rend en rsultat le tableau 2 dimension :

qui reprsente la matrice :

1 0 0 0
0 1 0 0

0 0 1 0
0 0 0 1

1
0
0
0

0
1
0
0

0
0
1
0

0
0
0
1

Spcification :
static int[][] matriceUnite(int n)
// rsultat
: reprsentation de la matrice unit de taille n
3 - Transpose : la fonction Transposee doit rendre en rsultat un tableau reprsentant la
matrice transpose de celle reprsente par un tableau carr M.
Rappel : la transpose dune matrice M est la matrice MT telle que MT[i,j] = M[j,i] (interversion
des lignes et des colonnes).

164

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

Exemple :
avec M reprsentant la matrice :

Transposee(M) reprsente la matrice :

12 6 4
9 1 0
3 15 8

12 9 3
6 1 15
4 0 8

Spcification :
static int[][] transposee(int[][] M)
// prrequis
: M est un tableau carr
// rsultat
: reprsentation de la matrice transpose
// de celle reprsente par M
4 - Test de diagonalit : la fonction estDiagonale doit indiquer si la matrice reprsente par un
tableau carr M est diagonale. Rappel : une matrice M est diagonale si seule les lments M[i,i] sont
diffrents de 0.
Exemples :
la matrice suivante est diagonale :

celle-ci nest pas diagonale :

12 0 0
0 1 0
0 0 8

12 0 0
0 1 15
4 0 8

Spcification :
static boolean estDiagonale(int[][] M)
// prrequis
: M est un tableau carr
// rsultat
: indique
si la matrice

reprsente

par

M est

5 - Carr magique
Une matrice carre dentiers est magique si les sommes des valeurs de chaque ligne, de chaque
colonne et des deux diagonales sont gales.
Exemple de carr magique :
6

La fonction estMagique doit indiquer si la matrice reprsente par un tableau M est magique.
Spcification :
static boolean estMagique(int[][] M)
// prrequis
: M est un tableau carr
// rsultat
: indique si la matrice reprsente par M est magique

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

165

diag

Tableaux plusieurs dimensions

Exercice 9.8

Calcul des nombres premiers par le crible dEratosthne

Lalgorithme suivant permet de dterminer les nombres premiers infrieurs n. Soit un tableau de
boolens premier , de taille n, initialis vrai.

On commence avec lindice k=2, aprs avoir mis premier[0] et premier[1] faux.
Si premier[ k]=vrai, on met faux tous les lments dindice multiple de k jusqu lindice
k2.
On incrmente k et on recommence tant que k est infrieur la racine carr de n.
Les nombres premiers sont alors les nombres i tels que premier[ i]=vrai.
Rdiger la fonction suivante selon ce principe :
static boolean[] premier(int n)
// prrequis
: n>=0
// rsultat
: tel que pour tout i <n,
// premier(n)[i] indique si i est premier

166

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

Aide 9.1

Diverses fonctions sur tableaux de chanes de caractres

Fonction enClair :
Il faut se mfier de placer les , uniquement entre deux lments de T. Pour cela on peut concatner
au rsultat ,lment pour chaque lment sauf pour le premier.
Encore faut-il que T possde au moins un lment. Nous acceptons que T soit vide (T.length =0)
et le rsultat est "{}" dans ce cas. Il convient de traiter ce cas sparment au dbut du corps de la
fonction.

Fonction estPresent :
T ntant pas priori tri, nous sommes oblig deffectuer un parcours des lments successifs.
Litration sarrte lorsquon a parcouru tout le tableau (i==T.length ) ou quand on rencontre un
lment gal s.

Fonction indiceDuPlusPetitAlphabetique :
La ralisation de cette fonction est similaire celle de la fonction indiceDeMax vue dans ce chapitre du cours. Les diffrences sont :

Lordre est lordre alphabtique, qui se teste par T[i].compareTo(min)<0 pour savoir si
T[i] est plus petit (avant par ordre alphabtique) que min.
On cherche le minimum sur la tranche T[k..T.length[ au lieu de la tranche T[0..k[ .

Procdure trier :
Cette procdure est similaire la procdure de tri dun tableau de nombres vue dans ce chapitre du
cours. La seule diffrence est que lon maintient trie la tranche T[0..i[ au lieu de la tranche
T[i..T.length[ car on procde par dplacement des lments minimaux successifs (au lieu
des lments maximaux).
0

partie trie

iMin

"pp"

"jj"

"jj"

"pp"

Module A.P.I. Algorithmique Programmation Imprative

T.length

Universit de Rennes 1

167

Tableaux plusieurs dimensions

Aide 9.2 - Tri dun tableau par dtermination des rangs des lments
1 - Fonction nombreDe
Il suffit de parcourir le tableau T en incrmentant un compteur resul chaque fois que llment
T[i] est gal v.
2 - Fonction rangDe
Il suffit de parcourir le tableau T en incrmentant un compteur resul chaque fois que llment
T[i] est strictement infrieur v.
3 - Fonction tri
Pour chaque lment T[i], on calcule son rang j et son nombre doccurrences n, puis on place
dans le tableau rsultat, partir de j, n occurrences successives de T[i].

Aide 9.3

Tri par permutations et tri bulle (Bubble sort)


1 - Tri par permutations

1.1 - Procdure monteeDeBulles .


Il faut parcourir le tableau depuis son dernier lment (i=T.length ) jusqu lindice k exclu
(i==k-1 , soit encore i<k) en ralisant un change entre T[i] et T[i-1] si T[i]<T[i-1] .
1.2 - Procdure trier.
Il suffit de solliciter pour i variant de 0 T.length-1 . A chaque itration, un lment de plus est
sa place dans T. En sortie ditration T est totalement tri.
2 - Tri bulle
2.1 - Procdure tenteMonteeDeBulles .
Le principe est le mme que celui de la procdure monteeDeBulles , mais avec en plus llaboration du rsultat boolen au moyen dune variable resul qui indique tout moment sil y a eu un
change sur la tranche dindices compris entre i et T.length .
2.2 - Procdure trier.
Elle utilise une variable boolenne pasTermine destine capter le rsultat de tenteMonteeDeBulles . Litration sarrte lorsque pasTermine devient faux, ce qui signifie quaucune
bulle na t monte par tenteMonteeDeBulles .

168

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

Aide 9.4

Tri par comptage

La procdure de tri par comptage commence par chercher le plus grand lment de T de manire
dterminer la taille du tableau de comptage nombreDOccurences . Ce tableau est cr avec cette
taille et initialis avec des 0 partout.
Ensuite, le tableau T est parcouru en comptant le nombre doccurrences de chaque valeur rencontre, cest--dire en incrmentant nombreDOccurences[ v] pour chaque valeur v rencontre.
Enfin le tableau nombreDOccurences est parcouru pour placer des squences successives de
nombreDOccurences[ i] valeurs gales i dans le tableau T.

Aide 9.5

Recherche dichotomique, version rcursive

La fonction indiceDe utilise une fonction auxiliaire rcursive rechercheDansTranche


dont voici les spcifications :
static int rechercheDansTranche(int i,int j,double[] T, double v){
// prrequis
: T est tri par ordre croissant
// rsultat
: un indice k tel que T[k]=v si v est prsent
// dans la trancheT[i..j], -1 sinon
Aide 9.6

Tri par fusion

Le tri consiste utiliser


che(T,0,T.length-1) .

triTranche

sur

tout

le

tableau,

soit :

triTran-

Fonction triTranche :
Le rsultat est un nouveau tableau de mme taille que la tranche. Il convient de crer ce tableau en
dbut du corps de la fonction :
int[] resul = new int[j-i+1];
Larrt de la rcursivit a lieu pour une tranche rduite un seul lment. Le rsultat est alors le
tableau constitu de ce seul lment : resul[0]=T[i]
Pour un tableau de plus dun lment, le dcoupage en deux parties de la tranche T[i,j] donne
les deux tranches T[i,(i+j)/2] et T[(i+j)2+1,j] .
Le tri de ces deux tranches (appels rcursifs) donne les tableaux tris T1 et T2.
Ces tableaux sont alors fusionn dans resul .
La fusion consiste piocher dans T1 ou dans T2 selon en prlevant chaque fois le plus petit lment parmi T1[k1] et T2[k2] .
La fusion se termine par un clusage dun des tableaux. En effet, le prlvement dans T1 ou T2
doit sarrter ds quun des tableau est puis. Il faut alors complter resul par les lments restant dans lautre tableau.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

169

Tableaux plusieurs dimensions

Aide 9.7

Manipulation de matrices

1 - Fonction enClair
Peu de difficult. Placer un passage la ligne, "\n", devant chaque visualisation de ligne.

2 - Fonction matriceUnite
Le rsultat est un tableau carr de taille n, cr par
int[][] resul = new int[n][n];
Deux boucles imbriques parcourent le tableau resul pour y placer 1 aux indices correspondant
la diagonale principale (i==j) et 0 ailleurs (i!=j).

3 - Fonction Transposee
Le rsultat est un tableau carr de mme taille que M, cr par
int n=M.length; int[][] resul = new int[n][n];
Deux boucles imbriques parcourent les tableaux M et resul pour appliquer la dfinition de ce
quest la transpose :
resul[i][j] = M[j][i]

4 - Fonction estDiagonale
Il suffit de vrifier que tout lment hors de la diagonale principale est nul.
5 - Fonction estMagique
Il est bon de se donner les fonctions intermdiaires suivantes, qui permettront de programmer de
faon aise et comprhensible la fonction estMagique . Ces fonction sont :

sommeLigne : la somme des lments dune ligne i de M,


sommeColonne : la somme des lments dune colonne j de M,
sommePremiereDiagonale : la somme des lments de la premire diagonale de M,
sommeDeuxiemeDiagonale : la somme des lments de la deuxime diagonale de M.
Disposant de ces fonctions, il suffit de calculer sommeLigne0 , la somme des lments de la ligne
0, puis de vrifier que cest aussi la somme des lments de chaque ligne, des lments de chaque
colonne et des lments des deux diagonales.

170

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Tableaux

Aide 9.8

Calcul des nombres premiers par le crible dEratosthne

Le tableau boolen rsultat, resul , est de taille n. Il faut linitialiser true, sauf resul[0] et
rsul[1] qui doivent tre positionns false .
Ensuite, pour chaque k depuis 2 jusqu n-1, si resul[k] vaut true il faut positionner
false les lments dindices multiples de k. Pour cela il convient dutiliser une boucle qui progresse par pas de k, de la forme :
for(int i=2*k; i<n; i=i+k){...}
Voici un exemple de procdure de test de cette fonction.
Ce qui nous intresse en fait ce nest pas dafficher le tableau de boolens qui indique si un nombre
est premier, mais dafficher la suite des nombres premiers infrieurs n. Pour cela nous avons
rdig une fonction static String
nombresPremiers(int ) qui rend
n en rsultat la chane
de caractres qui visualise en clair cette suite.
static String nombresPremiers(int n){
// rsultat
: la suite en clair des nombres premiers < n
boolean[] estPremier = premier(n);
String resul="";
for(int i=0; i<n; i++){
if(estPremier[i]){resul=resul+i+" ";}
}
return resul;
}
public static void main(String[] z){
System.out.println("nombres premiers < 100
System.out.println(nombresPremiers(100));
}

:");

La procdure principale de test utilise cette fonction pour imprimer la suite des nombres premiers
infrieurs 100. Cela doit donner :
2 3 5 7 1 1 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 8
97

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

171

Tableaux plusieurs dimensions

172

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Objets de type classe - abstraction

CHAPITRE 10

Objets de type classe - abstraction

La notion de structure permet de dfinir un type de donne qui possde plusieurs caractristiques
reprsentes par les valeurs des champs de la structure. On va maintenant tendre cette notion en
permettant de regrouper non seulement des valeurs mais galement des oprations dans la dclaration de type. Un tel regroupement de donnes (attributs) et doprateurs (mthodes) sappelle un
objet. Un type dobjet est dfini au moyen dune classe.

10.1

Approche variable de type structure - approche objet


Lorsque lon conoit un logiciel, on est amen modliser certains objets du domaine de lapplication. Considrons un exemple trs lmentaire : un robot simpliste. Un tel robot occupe une certaine
position (X,Y), il a une orientation parmi {nord, est, sud, ouest}, il est initialis avec une position et
une orientation donnes, il peut tourner droite, il peut avancer dun pas... On le dcrira par une
association de variables qui reprsentent son tat et des procdures qui modlisent son comportement.

nord
Y

ouest

est
sud

10.1.1

Approche variable structure


Une faon de faire que nous avons dj pratique est la suivante :

dune part on utilise un type structure, compos de champs de donnes, pour reprsenter les
caractristiques de lobjet modliser, un robot dans notre exemple :

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

173

Approche variable de type structure - approche objet

donnes

Y
orientation

class Robot {
public int X; public int Y;
public Orientation o;
public Robot(int x, int y, Orientation versOu) {
X=x; Y=y; o=versOu;
}
}
avec, pour lorientation : enum Orientation {nord,est,sud,ouest }

dautre part, on rdige les procdures qui ralisent le comportement attendu :


avancer
procdures

tournerADroite

class utilisationDeRobot {
static void avancer(Robot ceRobot) {
switch (ceRobot.o) {
case nord
: ceRobot.Y = ceRobot.Y+1; break;
case est
: ceRobot.X = ceRobot.X+1; break;
case sud
: ceRobot.Y = ceRobot.Y-1; break;
case ouest: ceRobot.X = ceRobot.X-1; break;
}
}
static void tournerADroite(Robot ceRobot){
switch (ceRobot.o) {
case nord
: ceRobot.o=Orientation.est; break;
case est
: ceRobot.o=Orientation.sud; break;
case sud
: ceRobot.o=Orientation.ouest; break;
case ouest: ceRobot.o=Orientation.nord
; break;
}
}

174

public static void main(String[] arg) {


Pour crer un robot baptis vigor initialis en (1,3) orientation nord,
puis le faire avancer dune position, il faut crire :
Robot vigor = new Robot(1,3,Orientation.nord);
avancer(vigor);
...
}

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Objets de type classe - abstraction

10.1.2

Approche objet
Une faon de faire plus modulaire consiste dfinir les oprations dans le texte de la classe qui dfinit le type de lobjet.
X

donnes

objet gnral

+
procdures

Y
orientation
initialisations
avancer
tournerADroite

Cela scrit ainsi :


class Robot {
variables qui reprsentent ltat dun Robot
public int X; public int Y;
public int o;
initialisations la cration dun Robot (constructeurs)
public Robot(int x, int y, Orientation versOu) { constructeur
X=x; Y=y; o=versOu;
}
public
Robot() constructeur
{
sans paramtre, initialise lorigine, orientation nord
X=0; Y=0; o=Orientation.nord;
}
oprations (mthodes dobjet)

public void avancer() {


switch (o) {
case nord
: Y=Y+1; break;
case st
: X=X+1; break;
case sud
: Y=Y-1; break;
case ouest: X=X-1; break;
}
}
public void tournerADroite(){
switch (o) {
case nord
: o=Orientation.est
; break;
case est
: o=Orientation.sud
; break;
case sud
: o=Orientation.ouest; break;
case ouest: o=Orientation.nord
; break;
}
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

175

Approche variable de type structure - approche objet

Avec cette nouvelle forme, on notera les diffrences suivantes :


1 - Lobjet sur lequel porte une opration, le paramtre principal ou paramtre implicite de
lopration, est plac devant, spar par un point. Ainsi, pour crer un robot baptis vigor initialis en (1,3) orientation nord, puis le faire avancer dune position, on crit :
Robot vigor = new Robot(1,3,Orientation.nord);
vigor.avancer();
2 - En plus du regroupement des oprations dans le texte de la classe, on notera une diffrence dans
la forme : le paramtre explicite ceRobot a disparu de la dfinition des oprations. Les oprations portent sur le paramtre implicite, objet du type de la classe, quon appelle instance courante. Une telle opration qui porte sur linstance courante sappelle mthode dobjet.
3 - On remarquera galement labsence du vocable static : les fonctions qui sont des mthodes
dobjet nont pas cet attribut. Seules les fonctions qui reoivent tous leurs paramtres de faon
traditionnelle par les parenthses ont lattribut static . On a maintenant lexplication de la
signification du vocable static :

Les composants statiques dune classe, variables, constantes ou fonctions affubls de

lattribut static , sont des composants qui existent en un seul exemplaire et qui ne sont
associs aucun objet particulier. Ctait le cas des fonctions utilises jusqu prsent ainsi
que des constantes et variables globales utilises dans certaines classes.
Les composants non statiques, cest--dire non affubls de lattribut static , sont des
composants qui existent dans chaque objet instance de la classe. Cest le cas des composants de donnes qui dcrivent lobjet (X, Y, o) et des mthodes dobjet (avancer() ,
tournerADroite() ).

4 - La forme de lappel est diffrente : lobjet sur lequel porte lopration nest pas indiqu entre
parenthses comme pour un paramtre explicite, mais devant le nom de lopration :
vigor.avancer() . Pendant lexcution de cet appel, linstance courante est lobjet dsign par vigor.
5 - La rdaction des oprations est galement un peu diffrente : les composants de linstance courante sont dsignes simplement par leurs identificateurs, X, Y, orientation , sans avoir
indiquer explicitement lobjet.

10.1.3

Citation explicite de linstance courante : this


Dans certains cas, on peut avoir besoin de citer explicitement linstance courante dans le texte dune
mthode dobjet, par exemple pour passer cette instance en paramtre dune procdure. On dispose
pour cela du vocable this (littralement celui-ci).
Pendant lexcution dune mthode dobjet, this dsigne linstance courante.
Par exemple, pendant lexcution de lappel vigor.avancer() , this dsigne lobjet dsign par vigor.
On peut, mais cest inutile et lourd, prfixer par this les noms des composants de linstance courante. Par exemple dans les mthodes de la classe Robot , this.X , this.Y et this.o signifient
la mme chose que X, Y et o.

176

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Objets de type classe - abstraction

10.1.4

Encapsulation - notion dabstraction


Lun des principaux soucis du programmeur est lexactitude de ses programmes : il faut pouvoir
assurer que seules les choses vraiment voulues puissent se produire. Pour cela un des moyens consiste interdire laccs incontrl aux donnes dun objet. Voici quelques exemples concernant la
classe Robot :

On dsire que la position dun Robot ne puisse tre modifie que par la procdure avan-

cer() , parce que, par exemple, le mouvement du robot que lon modlise ne peut se faire que
dune unit dans sa direction courante.
On dsire que lorientation dun Robot ne puisse tre modifie que par la procdure tournerADroite() , parce que le robot ne peut faire que des rotations dun quart de tour droite.

En rgle gnrale, on dsire cacher les donnes concrtes qui reprsentent ltat dun objet car
ces donnes concrtes, les entiers X, Y et orientation dans lexemple du robot, rsultent dun
choix plus ou moins arbitraire pour reprsenter lobjet et ne font pas partie des spcifications logiques de lobjet. Par exemple, pour fixer les ides, on pourrait trs bien reprsenter concrtement la
position du robot non pas par son abscisse X et son ordonne Y, mais par sa distance lorigine R et
langle Teta de son rayon vecteur avec laxe Ox, ou toute autre reprsentation.

Y
R
Teta
X
Tout ce qui reprsente correctement ltat du robot peut convenir et cest au programmeur de faire
son choix pour diverses raisons de simplicit de calcul ou de facilit de programmation. Dans tous
les cas, vu de lextrieur, le robot doit se comporter pareillement. Les proprits du robot doivent
tre les mmes quelque soit la faon dont il est concrtement reprsent. Ces proprits intrinsques
du robot constituent ce quon appelle labstraction du type Robot . On ne va pas dvelopper ici une
thorie des robots qui avancent dun pas et tournent droite car ce genre de thorie est inintressante et par trop spcialise. Mais pour bien saisir ce quest une telle abstraction voici une proprit
que possde tout objet de type Robot :
si on excute la squence doprations :
avancer(), tournerADroite(), avancer(), tournerADroite(),
avancer(), tournerADroite(), avancer(), tournerADroite()
le robot se retrouve dans la mme position et la mme orientation quavant la squence.

Une telle proprit est intrinsque : elle est totalement indpendante de la reprsentation concrte
choisie.

Puisque les donnes concrtes ne font pas parties des proprits abstraites de lobjet, il est malsain
de permettre laccs ces donnes. Ces donnes nexistent que par hasard, cause du choix arbitraire de reprsentation.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

177

Approche variable de type structure - approche objet

La technique usuelle, appele encapsulation, consiste interdire laccs direct aux donnes des
objets en dehors des programmes de la classe. En Java cela se note par le vocable private .
Lidentificateur dun composant de la classe, que ce soit une donne ou une fonction, affubl du
qualificatif private peut tre utilis uniquement depuis les textes de programme de la classe.
Notion daccesseur
Dans lexemple de la classe Robot , les donnes concrtes X, Y et orientation doivent tre
caches. Cependant il existe bien ici des correspondants directs X, Y et orientation qui font partie
de labstraction robot : un robot possde vraiment une abscisse X, une ordonne Y et une
orientation orientation. Dans ce cas on rdige des mthodes dont le seul rle est doffrir un accs
ces informations. Ces mthodes sont gnralement triviales : elles rendent en rsultat la valeur dun
composant de lobjet, ou affectent un composant de lobjet, parfois aprs avoir opr quelques vrifications de consistance. Ces mthodes sappellent des accesseurs. La convention la plus rpandue
est dappeler getXXX la mthode qui permet de lire la proprit XXX de lobjet et setXXX celle
qui permet de laffecter. Avec ces conventions, une rdaction de la classe Robot devient :
class Robot {
donnes concrtes caches
private int X; private int Y; private Orientation o;
constructeurs
public Robot(int x, int y, Orientation versOu) {...}
public Robot() {...}

oprations
public void avancer() {...}
public void tournerADroite(){...}
accesseurs
public int getX() {return X;}
public int getY() {return Y;}
public int getOrientation() {return o;}

On na fourni ici que des accesseurs en lecture, car on veut que les mouvements soient limits
ceux dfinis par avancer et tournerADroite .
Pour obtenir labscisse du robot vigor, il suffit dappeler la mthode : vigor.getX() . Lcriture vigor.X est interdite (en dehors du texte de la classe Robot ) : cest une erreur de syntaxe.
Rappels sur la nature des objets
Nous avons dj expliqu la nature des objets propos des cas particuliers que sont les chanes de
caractres (type String ) et les structures, objets sans mthodes dont les composants de donnes
ont le qualificatif public .
Les objets dfinis par un type classe ont les mmes prrogatives :

Il sont crs au moyen de lexpression : new nomDeLaClasse(paramtres du constructeur)


Ils sont toujours dsigns par rfrence.
La valeur des variables, paramtres et rsultats de type classe est toujours une rfrence un
objet de ce type. La dclaration dune variable de type classe initialise sa valeur null, rfrence qui ne dsigne rien.
Robot vigor;

178

Module A.P.I

vigor

Algorithmique Programmation Imprative

null

Universit de Rennes 1

Objets de type classe - abstraction

vigor = new Robot(3,12,Orientation.ouest);

X: 3
Y: 12
o: ouest

vigor

espace des objets

Laffectation dune variable de type classe est laffectation dune rfrence la variable. Lobjet
anciennement rfrenc nest pas affect.

vigor=totor;

avant

aprs

vigor

X: 3
Y: 12
o: ouest

vigor

X: 3
Y: 12
o: ouest

totor

X: 4
Y: 2
o: nord

totor

X: 4
Y: 2
o: nord

Loprateur de comparaison == entre expressions de type classe compare les rfrences et

non pas les valeurs des objets rfrencs. Le rsultat est vrai si et seulement si les rfrences
dsignent le mme objet.

vigor
totor

X: 3
Y: 12
o: ouest

fulgor

X: 3
Y: 12
o: ouest

vigor==totor est vrai


vigor==fulgor est faux

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

179

Exemple rcapitulatif

10.2

Exemple rcapitulatif
La chasse au trsor :
On suppose quun trsor est plac un certain endroit (x0,y0) du plan. Un robot modlis par la
classe Robot prcdemment prsente se trouve initialement en (0,0). Le but du jeu est de piloter
le robot de faon lamener au mme endroit que le trsor. Le droulement de ce jeu peut tre prcis ainsi :
choisir au hasard la position (x0,y0) du trsor (sans la communiquer au joueur),
fixer la position initiale du robot en(0,0), orientation nord,
rpter tant que le robot nest pas sur le trsor {
indiquer au joueur la distance entre le robot et le trsor,
obtenir un ordre tourner droite ou avancer de la part du joueur et lexcuter,
}
afficher gagn et la position du trsor
Pour tirer des nombres au hasard on dispose dans la bibliothque standard de Java de la classe
Random . Un objet de cette classe est un gnrateur alatoire de nombres. Il possde une mthode
nextInt(k) qui rend en rsultat un nombre entier tir au hasard compris entre 0 et k (exclu).
class ChasseAuTresor {
// position du trsor (variable globale)
static int x0; static int y0;
// le robot (variable globale)
static Robot leRobot;
static int carreDistanceAuTresor() {
// rsultat : carr de la distance du robot au trsor
int dx=leRobot.getX()-x0; int dy=leRobot.getY()-y0;
return dx*dx + dy*dy;
}

180

public static void main(String[] arg) {


// choisit au hasard la position (x0,y0) du trsor
Random generateurAleatoire= new Random();
x0=generateurAleatoire.nextInt(10);
y0=generateurAleatoire.nextInt(10);
// cre le robot en (0,0), orientation nord
leRobot = new Robot();
// rpte tant que le robot nest pas sur le trsor
while(carreDistanceAuTresor()!=0) {
// indique
au joueur
la distance
entre le robot et le trso
System.out.print("distance="+carreDistanceAuTresor());
// obtient
un ordre 'a' (avancer)
ou 't' (tourner
droite
System.out.print(" ordre
:");
char ordre=Lecture.unCar();
// excute lordre
if (ordre==a) {leRobot.avancer();}
else /*ordre==t*/ {leRobot.tournerADroite();}
}
// fin de partie
System.out.println("gagn,
le trsor est en <"+x0+","+y0+">");
}

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Objets de type classe - abstraction

10.3

Retour sur les composants statiques et non statiques


Toutes les choses en Java sont rdiges lintrieur dune classe : constantes, variables, fonctions
sont toujours dfinies dans une classe. La classe est lunit de programmation.
Le premier rle de la classe est un rle de modularit : regrouper les choses concernant un mme
thme. Ce premier aspect est simple mais cependant important car, dans une programmation complexe, cest en rangeant convenablement les choses quon parvient sy retrouver. Les choses qui
existent en permanence et sont associes de faon unique leur nom sont dites statiques. Elles sont
dclares au niveau 0 de la classe avec le vocable static .
Le deuxime rle ventuel dune classe est dtre un modle dobjets. Un modle dobjets est en
quelque sorte un moule qui sert frabriquer des exemplaires similaires, appeles instances de la
classe, grce lopration new. Dans ce cas la classe possde des composants qui doivent exister en
association avec chaque instance. Ces composants nexistent pas initialement. Un exemplaire est
cr pour chaque instance cre. Ces composants sont non-statiques (on pourrait galement dire
dynamiques)
Ce qui surprend avec un langage objets, tel que Java, est que les choses statiques, qui sont les choses les plus simples, ncessitent le vocable static alors que les choses non-statiques, qui sont un
peu plus compliques, ne ncessitent aucun vocable particulier. Ceci est d au fait quun langage
objets veut favoriser la programmation en terme dobjets, donc de classes modles dobjets. Dans
ce cas, les composants non-statiques sont plus frquents que les composants statiques, et une loi
naturelle veut quun langage soit bref pour dire les choses les plus frquentes.
Exemple :
Lexemple simple suivant est une classe qui est un modle dobjets pour reprsenter des personnes.
Chaque personne est caractrise par un nom et une taille. Les composants nom et taille sont
propres chaque individu : ce sont des variables non-statiques.
Il existe une notion de taille normale adulte concrtise par une constante. La constante
tailleNormaleAdulte existe en un seul exemplaire et depuis le dbut de lexcution du
programme : cest une constante statique.
Une variable plusGrandeTaille mmorise en permanence la plus grande des tailles des personnes existantes. Cette variable existe bien sr en un seul exemplaire, et depuis le dbut de lexcution du programme, initialise 0 (plus exactement llment neutre de la fonction maximum
sur les tailles) : cest une variable statique.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

181

Retour sur les composants statiques et non statiques

class Personne {
public static final int tailleNormaleAdulte = 170;
private static int plusGrandeTaille = 0;
private String nom;
private int taille;
public Personne(String ceNom, int cetteTaille) {
nom = ceNom; taille = cetteTaille;
if (cetteTaille > plusGrandeTaille) {
plusGrandeTaille=cetteTaille;
}
}
public String getNom() { return nom; }
public int getTaille() { return taille; }
public setTaille(int nouvelleTaille) {
taille = nouvelleTaille;
if (nouvelleTaille > plusGrandeTaille) {
plusGrandeTaille=nouvelleTaille;
}
}
}

public static getPlusGrandeTaille { return plusGrandeTaille;}

Pour viter des affectations incohrentes de la variable plusGrandeTaille , celle-ci a t dclare prive (private ). Ainsi elle ne peut tre cite que depuis le texte de la classe Personne ,
loccasion du constructeur et de la mthode setTaille de changement de taille.
Pour pouvoir accder la valeur de cette variable depuis lextrieur de la classe, on a donc programm un accesseur getPlusGrandeTaille .
Cet accesseur ne sapplique pas une instance particulire de Personne , cest donc une fonction
statique. Depuis lextrieur de la classe Personne on lappelle sous la forme
Personne.getPlusGrandeTaille() .

Voici un exemple de programme qui utilise la classe Personne :

182

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Objets de type classe - abstraction

class TestPersonne {
public static void main(String[] x) {
// cration de trois personnes
Personne jules = new Personne("Jules", 135);
Personne alfred = new Personne("Alfred", 183);
Personne marcel = new Personne("Marcel", 168);
int k = Personne.getPlusGrandeTaille()
System.out.print("la plus grande taille est " + k);

jules.setTaille(183);
int k = Personne.getPlusGrandeTaille()
System.out.print("la plus grande taille est " + k);
if (k>Personne.tailleNormaleAdulte) {
System.out.print("cest plus que la normale");
}

tailleNormaleAdulte

170

constante statique

plusGrandeTaille

183

variable statique

nom:"Jules"
taille: 135

nom:"Alfred"
taille: 183

thme des personnes


class Personne

instances de Personne

nom:"Marcel"
taille: 168

jules
alfred
marcel

10.4

variables locales du programme principal

Commentaires de spcification pour une classe modle dobjets


Les fonctions et les classes permettent de raliser des abstractions. Les services offerts par une
fonction ou une classe doivent tre clairement (et si possible formellement) dfinis. Dans les programmes, ceci se fait au moyen de commentaires de spcification. Ces commentaires se placent
avec len-tte de chaque fonction, comme nous lavons fait systmatiquement ici. Ces commentaires comportent trois rubriques (pas ncessairement toutes prsentes) : prrequis, effet et rsultat .

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

183

Commentaires de spcification pour une classe modle dobjets

Rubrique prrequis

// prrequis

condition
:

Cette rubrique indique les conditions ausquelles doivent satisfaire les paramtres de la fonction et
ltat de lobjet (ltat de this) lorsquil sagit dune mthode dobjet. La condition est donc une
formule logique (un prdicat). Cette condition na pas tre teste par le programme de la
fonction : il est de la responsabilit de lutilisateur de respecter cette condition. En dautres termes,
tant pis pour lutilisateur sil ne respecte pas le prrequis, la fonction peut faire nimporte-quoi dans
ce cas sans que le programmeur de la fonction en soit responsable.
Dans le cas dune mthode dobjet, ltat de lobjet (this) ne doit faire en aucune faon usage des
composants de lobjet, qui ne sont que des moyens concrets de mise en uvre. Il ne faut utiliser que
les notions abstraites de lobjet.
Par exemple, pour une liste de personnes reprsente par un tableau et une variable comptabilisant
le nombre dlments de la liste :
class ListeDePersonnes {
private Personne[] lesElements; private int nbElements;
public ListeDePersonnes() { // liste vide
lesElements= new Personnne[1000]; nbElements=0;
}
...
public Personne laPremiere(){
// prrequis
: this nest pas vide
// rsultat
: la premire personne de this
}
...
}
Le prrequis this nest
pas vide
, est acceptable car tre vide fait partie de la notion
abstraite de liste. La condition nbElements>0 serait en revanche innaceptable car elle fait rfrence la mise en uvre.
Rubrique effet

// effet

:
changements dtat ou interractions avec lextrieur

Ici encore, pour une mthode dobjet qui change ltat de this, ou une fonction qui change ltat
dun objet de la classe pass en paramtre, le changement dtat doit tre indiqu en termes de
labstraction reprsente par la classe, et surtout pas en terme de changement dtat des composants. Par exemple, pour une classe ListeDePersonne , la mthode qui permet dajouter une
personne la liste serait ainsi spcifie :
public void ajouter(Personne p) { // effet
lesElements[nbElements]=p; nbElements++;
}

: ajoute p this

Et surtout pas : range p dans lesElements[nbElements] et incrmente nbElements car


les composants lesElements et nbElements ne font pas partie des choses sensibles lutilisateur. Ce ne sont que des moyens de mise en uvre, ils pourraient ne pas exister avec un autre mise
en uvre.
Rubrique rsultat

// rsultat

expression
:
indiquant le rsultat rendu

lexpression qui indique ce que vaut le rsultat ne doit pas faire rfrence aux moyens utiliss pour
le calculer. Il ne doit faire rfrence quaux paramtres, ltat abstrait de this (pour une mthode
dobjet) ou dun paramtre de la classe, et toutes les fonctions connues par ailleurs, mathmatiques ou spcifiques au domaine trait.

184

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Objets de type classe - abstraction

Exercice 10.1

Reprsentation des complexes au moyen dune (vraie) classe

Dans un exercice prcdent nous avions reprsent les nombres complexes au moyen dune structure. Ce nest pas la bonne faon de faire. Il faut totalement abstraire les donnes concrtes qui
reprsentent un nombre complexe (x et y dans cet exemple), rdiger les fonctions de manipulation
des complexes lintrieur de cette classe et ne rendre visible aux utilisateurs que ces fonctions.
Rdiger la classe Complexe dans cet esprit.
Pour satisfaire aux gots divers et varis des utilisateurs potentiels, on pourra raliser deux versions
de chaque fonction de calcul :
une version fonction statique, par exemple :
public static Complexe add(Complexe z1, Complexe z2)
// rsultat : la somme de z1 et de z2
une version mthode dobjet :
public Complexe add(Complexe z)
// rsultat : la somme de this et de z

Exercice 10.2

Reprsentation de personnes

Rdiger une classe Personne qui reprsente une personne dote des attributs suivants : nom,
sexe, adresse, anne de naissance.
Le nom et ladresse sont des chanes de caractres (quelconque, on ne cherche pas vrifier la vraisemblance dtre un nom ou une adresse). Le sexe a deux valeurs possibles, masculin ou feminin. Lanne de naissance est donne sous la forme dun entier.
Les composants de donnes seront privs. On donne accs en consultation ces attributs au moyen
daccesseurs.
La classe doit offrir :
un constructeur trivial, qui reoit en paramtres la valeurs des attributs (nom, sexe, adresse et anne
de naissance),
Une mthode toString qui rend en rsultat une chane de caractres qui visualise en clair les
attributs de la personne.
Une fonction statique laPlusJeune telle que, p1 et p2 tant des personnes, laPlusJeune( p1,p2) est la personne la plus jeune parmi p1 et p2 (p1 en cas dgalit des ges).
Tester cette classe dans un programme principal (TestPersonne ) qui cre une personne toto,et
une personne x et imprime la plus jeune de ces deux personnes.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

185

Commentaires de spcification pour une classe modle dobjets

Exercice 10.3

Abstraction : collection de personnes

On dispose dune classe Personne ainsi spcifie :


class Personne { // reprsentation dindividus
public Personne(String n, int s, String adr, int an)
// construit une personne de nom n, de sexe s, dadresse adr
// et danne de naissance an
public static Personne lire()
// effet
: lit au clavier les caractristiques dune personne
// rsultat
: une nouvelle personne avec ces caractristiques
public String toString()
// rsultat
: la chane "en clair" caractrisant la personne
public int getSexe()
// rsultat
: sexe de this
public String getNom()
// rsultat
: nom de this
public String getAdresse()
// rsultat
: adresse de this
public int getAnneeNaissance()
// rsultat
: anne de naissance de this

public static Personne laPlusJeune(Personne p1, Personne p2)


// rsultat
: la personne la plus jeune de p1 et p2,
// p1 en cas dgalit

Une collection de personnes est un objet qui permet denregistrer un nombre quelconque dlments de type Personne . Une telle collection sera ralise par la classe Population dont voici
la spcification :
class Population{
public Population()
// constructeur

: population vide

public void ajouter(Personne p)


// effet
: ajoute p this (ne fait rien si p sy trouve dj)
public int cardinal()
//rsultat
: le nombre de personnes dans this

186

public Personne obtenir(int i)


// prrequis
: 0<=i<nbPersonnes()
// rsultat
: la ime personne de la collection. Les personnes
// ne sont pas classes, mais on garantit que obtenir(i) est
// toujours la mme personne et que si i!=j,
// obtenir(i)!=obtenir(j)

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Objets de type classe - abstraction

Mise en uvre
On dcide de reprsenter une population au moyen dun tableau personnes dlments de type
Personne et dune variable entire nbPersonnes .
Correspondance abstrait-concret : La variable nbPersonnes indique combien de personnes sont
dans la collection. Les personnes prsentes dans la collection sont ranges dans le tableau personnes depuis lindice 0 jusqu lindice nbPersonnes-1 . Le tableau personnes doit toujours avoir la taille suffisante pour contenir les personnes de la collection. Pour cela, il est cr
initialement avec une taille arbitraire tailleInitiale=5 , puis chaque fois que la taille du
tabeau devient insuffisante, on le remplace par un tableau de taille double.
un objet de type Population
personnes
nbPersonnes

0
1
2
3
4

objets de type Personne


toto
maryse
alfred

Rdiger cette mise en uvre.


Exercice 10.4

Abstraction : reprsentation des nombres rationnels

On se propose de rdiger une classe Rationnel qui reprsente les nombres rationnels (cest-dire les fractions p/q) dote des oprations classiques : somme, produit, quotient, test dgalit.
Voici la spcificationde la classe Rationnel :
class Rationnel {
public Rationnel(int a, int b)
// prrequis
: b!=0 (a et b de signe quelconque)
// constructeur
: cre un rationnel gal a/b
public Rationnel(int a)
// constructeur
: cre un rationnel gal a
public String toString()
// rsultat
: chane de caractre figurant this,
//
par exemple "-235/12" ou "235"
public static boolean egal(Rationnel x, Rationnel y)
// rsultat
: indique si x et y sont gaux
public static Rationnel somme(Rationnel x, Rationnel y)
// rsultat
: somme de x et y
public static Rationnel produit(Rationnel x, Rationnel y)
// rsultat
: produit de x par y

public static Rationnel quotient(Rationnel x,Rationnel y)


// prrequis
: y diffrent de zro
// rsultat
: quotient de x par y

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

187

Commentaires de spcification pour une classe modle dobjets

Mise en uvre :
On dcide de reprsenter un rationnel au moyen de deux entiers, le numrateur p et le dnominateur
q. Pour simplifier, les entiers seront raliss au moyen du type int.
Afin de faciliter le test dgalit et pour profiter au maximum de lintervalle de reprsentation des
entiers, on impose les contraintes suivantes sur p et q :
Le dnominateur q est >0. Le signe du nombre rationnel est donc celui de lentier p (qui est quelconque).
Le rationnel 0 est reprsent par p=0 et q=1.
Pour un rationnel diffrent de 0, p et q sont tels que la fraction p/q est irrductible (p et q sont
premiers entre eux). Par exemple, 27/18 devra tre simplifi en 3/2.
Rdiger cette mise en uvre.

Exercice 10.5

Abstraction : tables de correspondance

On se propose de raliser une structure de donnes table de correspondance permettant dassocier


des valeurs des cls. Les cls seront des chanes de caractres et les valeurs des nombres entiers.
Ceci permet par exemple de reprsenter le rsultat dune course en prenant pour cls les noms des
coureurs et pour valeurs les temps raliss :
<("dupont",28), ("alfred",45), ("durand",12)>
La classe TableDeCorrespondance doit offrir les fonctionnalits suivantes :
Constructeur permettant de crer une table vide.
Cration dune association (cl,valeur). Si la cl se trouve dj dans la table, cela remplace
lancienne valeur associe par la nouvelle.
Retrait dune association indique par sa cl. Ne fait rien si la cl est absente.
Recherche de la valeur associe une cl. Si la cl est absente, le rsultat est une valeur conventionnelle valeurAbsente , par exemple 1000000000.
Commencer par rdiger les spcificationsde cette classe : donnes, constructeur et mthodes publiques avec commentaires prcis indiquant leur fonctionalits.
Mise en uvre
On dcide de reprsenter une table de correspondance au moyen dun tableau T de paires
(cl,valeur) et dune variable entire nbElements qui indique le nombre dassociations prsentes dans la table. Les paires seront ralises au moyen dune classe interne Paire . La taille du
tableau, constante statique, fixera la capacit maximum de la table. Un dpassement de capacit
provoquera limpression dun message derreur.

188

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Objets de type classe - abstraction

On rdigera une fonction auxiliaire :


int indiceDeCle(String

c)

qui rend lindice de lassociation de cl c si elle existe, ou nbElements si la cl c est absente.


Test
Rdiger un programme principal de test qui
construit, par ajouts successifs, la table :
<("dupont",28), ("alfred",45), ("durand",12)>
recherche les valeurs associes "durand" et "toto" ,
modifie la valeur associe "durand" , supprime "alfred" ,
ajoute lassociation ("toto",66) ,
recherche les valeurs associes "durand" , "alfred" et "toto" .

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

189

Commentaires de spcification pour une classe modle dobjets

190

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : listes

CHAPITRE 11

11.1

Structures de donnes : listes

Notion de structure de donnes


Parmi les types de donnes on distingue ceux quon appelle des structures de donnes. Comme le
terme lindique, une structure de donnes permet de structurer des donnes : elle offre une gamme
doprations pour emmagasiner des donnes et une gamme doprations pour retrouver les
donnes emmagasines. titre dexemples voici quelques types usuels qui mritent lappellation
structure de donnes, accompagns dune description approximative des oprations quils
offrent :

tableau (ou, plus gnralement vecteur) : suite de donnes accessibles directement au moyen
dun indice entier.

liste : suite de donnes accessibles lune aprs lautre selon leur rang dans la suite.
ensemble : collection de donnes en vrac telle que toutes les donnes ont une valeur diff-

rente, dote de lopration test dappartenance et dun moyen de parcourir la collection.


table de correspondance : collection de paires de la forme <cl,valeur> qui permet de trouver la
valeur associe une cl.
arbre : collection de donnes appeles nuds, composs dune valeur et dune collection de
liens vers des nuds appels fils.
table de
arbre
vecteur
ensemble correspondance
"toto"
liste
<
"toto",12
>
0 12
12
2
<"tata", 2 >
12
2
25
12
1
2
"tutu"
"tata"
25
2 25
<"tutu",25 >
3 12
<"zozo",12 >
"zozo"

"zaza"

Chacune de ces structures de donnes donne lieu :

Une dfinition abstraite, encore appele spcification, qui dcrit les oprations quelle offre,

sans aucune allusion la moindre ralisation concrte des mcanismes mis en uvre pour raliser ces oprations.
Une ou plusieurs ralisations concrtes, encore appeles mises en uvre1, au moyen de donnes de types existants et de procdures.
Pour la mise en uvre, on utilise un type classe qui permet de dfinir des objets abstraits. Nous
avons dj vu que lencapsulation permet disoler la mise en uvre et lutilisation.
1. On dit implementation en anglais, et on dit implmentation (avec accent sur le e) en franglais.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

191

Spcification du type liste

11.2

Spcification du type liste


On sintresse ici des listes modifiables: une liste est un objet dont ltat est une suite finie, ventuellement vide, de donnes de mme type : e0, e1... en-1
Le nombre dlments, n, sappelle la longueur de la liste. La longueur de la liste vide est nulle.
La position 0 sappelle la tte, la position n-1 sappelle la queue.
Pour se positionner sur les lments et accder ces lments on utilise un objet spcifique que
nous appellerons un parcours de liste ou plus simplement parcours. Ltat dun parcours est :

soit une position dans la liste (0... n-1)


soit un tat particulier, fin de parcours, qui ne correspond aucune position (avant le dbut ou
aprs la fin).

position 2

tte

e
queue

fin de parcours

tat de parcours

Au sujet dun parcours on peut :

le positionner en tte de la liste (position 0 si elle existe, fin de parcourssi la liste est vide)
le positionner en queue de la liste (position n-1 si elle existe, fin de parcourssi la liste est vide)
passer la position suivante de la position courante. Ceci positionne en fin de parcourssi le
parcours est en queue.

passer la position prcdente de la position courante. Ceci positionne en fin de parcourssi le


parcours est en tte.
tte
a

queue
d

suivant

192

prcdent

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : listes

tant donn un tat de parcours, on peut :

ajouter un lment par insertion la suite de la position courante du parcours. Ltat de parcours

se positionne alors sur le nouvel lment.


supprimer llment courant du parcours. Ltat de parcours se positionne alors sur llment
qui suivait llment supprim, ou en fin de parcourssi le parcours tait en queue.
modifier la valeurde llment courant du parcours,
consulter la valeur de llment courant du parcours.
Plus prcisment, voici les mthodes offertes par les classes ListeDEntiers (listes) et Parcours (parcours de listes).
Nous prsentons ici des listes dentiers. Mais bien videmment on peut imaginer les mmes fonctionnalits pour des listes de nimporte quel type dlments.

public class ListeDEntiers {


public class Parcours {

//------------------------------------

public void tete()


// effet
: positionne
this en tte, estEnFin si liste

vide

public void queue()


// effet
: positionne
this en queue, estEnFin si liste

vide

public void suivant()


// prrequis
this
: non estEnFin
// effet
: positionne
this sur llment suivant
public void precedent()
// prrequis
this
: non estEnFin
// effet
: positionne
this sur llment prcdent
public boolean estEnFin()
// rsultat
: indique this
si est en fin de parcours
public int elementCourant()
// prrequis
this
: non estEnFin
// rsultat
: llment courantthis
de
public void modifElement(int nouvelElement)
// prrequis
this
: non estEnFin
// effet
: remplace llment courant de
this par nouvelElement
public void ajouteElement(int nouvelElement)
// effet : insre
nouvelElement aprs llment
courant
this,de
// en tte si estEnFin , positionne this sur llment ajout
public void retireElement()
// prrequis
this
: non estEnFin
// effet
: retire llment courant de
this, le suivant de
// llment retir, sil existe, devient llement courant,
// sinon this passe en fin de parcours
} // ---------------------------------------------------------

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

193

Spcification du type liste

public ListeDEntiers()

// constructeur

public boolean estVide()

: liste vide

// indique si this est vide

public void ajouteEnTete(int nouvelElement)


// effet
: ajoute
nouvelElement en tte
public void ajouteEnQueue(int nouvelElement)
// effet
: ajoute
nouvelElement en queue
public int retireEnQueue()
// prrequis
this
: nest pas vide
// effet
: retire de
this son dernier lment
// rsultat
: llment retir
public int retireEnTete()
// prrequis
this
: nest pas vide
// effet
: retire de
this son premier lment
// rsultat
: llment retir
public String toString()
// rsultat
: reprsentation en clairthis,
de
// de la forme
: < 12 | 23 | 45 | 12 | 6>, <> pour la liste vide
public static ListeDEntiers aPartirDe(int[] T){
// rsultat
: une nouvelle liste contenant les lmentsT de
public Parcours nouveauParcours()
// rsultat
: un nouveau parcours initialis en ttethis
de

public Parcours nouveauParcours(Parcours p)


// rsultat
: un nouveau parcours initialis dans ltatp de

Pour des raisons de modularit, la classe Parcours est une classe interne la classe ListeDEntiers . Nous verrons plus loin la notion de classe interne : cest simplement une classe dfinie
lintrieur dune autre classe.

194

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : listes

11.3

Exemples dutilisation dune liste

class utilisationDeListe {
public static void main(String[] arg) {
Dclaration et cration dune liste initialise vide :
ListeDEntiers uneListe = new ListeDEntiers();
Introduction de valeurs par la mthode ajouteEnQueue. Les instructions suivantes
construisent une liste contenant les carrs des nombres entiers de 1 9 :
for (int i=1; i<=9; i++) {uneListe.ajouteEnQueue(i*i);}
Recherche dans la liste du plus grand multiple dun nombre donn.
Pour cela il suffit de parcourir la liste partir de la queue.
System.out.print("recherche
du plus grand multiple
de j= ")
int j = Lecture.unEntier();
ListeDEntiers.Parcours p = uneListe.nouveauParcours();
p.queue();
while (!p.estEnFin()&&p.elementCourant()%j!=0) {
p.precedent();
}
Cet exemple illustre lutilisation dun parcours de liste :
la classe Parcours tant interne la classe ListeDEntiers, depuis lextrieur de la
classe il faut la nommer par :
ListeDEntiers.Parcours
Aprs la boucle, si le parcours p est en fin, cela signifie quil ny a pas de multiple dej.
Sinon llment courant du parcours p est la valeur cherche :
if (p.estEnFin()) {
System.out.println(j+"
na pas de multiple
dans la liste");
}
else {
System.out.print("plus grand multiple de "+j+ "
:);
System.out.println(p.elementCourant());
}
Modification dlments :
lexemple suivant ajoute 1 aux multiples de 3 dans la liste.
p.tete();
while (!p.estEnFin()) {
int v=p.elementCourant();
if (v%3==0) {p.modifElement(v+1);}
p.suivant();
}
Impression en clair de la liste :
System.out.println(uneListe.toString());
Suppression dlments :
suppression de tous les multiples de 4 de la liste :
p.tete();
while (!p.estEnFin()) {
if (p.elementCourant()%4==0) {p.retireElement();}
else {p.suivant();}
}
}
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

195

Exemples dutilisation dune liste

Autres exemples : fonctions manipulant des listes


Comme tout objet, une liste peut tre passe en paramtre ou rendue en rsultat de fonction. Nous
donnons dans ce qui suit, quelques exemples de fonctions qui manipulent des listes.
class DiversesProceduresManipulantDesListes {
static ListeDEntiers lectureListe() {
// effet
: lit au clavier des entiers termins par -1
// rsulat
: une nouvelle liste contenant les entiers lus
ListeDEntiers resultat = new ListeDEntiers();
int k=Lecture.unEntier();
while (k!=-1) {
resultat.ajouteEnQueue(k);
k=Lecture.unEntier();
}
return resultat;
}
static boolean estDansListe(int k, ListeDEntiers L) {
// rsultat
: indique si k est un lment de L
boolean kTrouveDansL=false;
ListeDEntiers.Parcours p= L.nouveauParcours();
while (!p.estEnFin() &&
!kTrouveDansL){
if(p.elementCourant()==k) {kTrouveDansL=true;}
else {p.suivant();}
}
return kTrouveDansL;
}
Procdure qui remet vide une liste.
static void viderListe(ListeDEntiers L) {
// effet
: rend L vide
while (!L.estVide()) {L.retireEnTete();}
}
public static void main(String[] arg) {
Test des procdures prcdentes :
System.out.println(
"entrer une squence dentiers termine par -1");
ListeDEntiers L = lectureListe();
if (estDansListe(12,L)) {
System.out.println("12 est dans la liste");
}
else {System.out.println("12 nest pas dans la liste");}
viderListe(L);
System.out.println("apres vidage, L = " + L.toString());
}
}

196

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : listes

11.4

Mise en uvre des listes


Les exemples prcdents montrent que lon peut utiliser les listes sans avoir la moindre ide de la
faon dont elles sont ralises. Nous insistons sur le fait quil nest pas utile de savoir comment
quelque chose est ralis pour pouvoir lutiliser : cest la base mme de toute abstraction. Il est
mme parfois prfrable de ne pas savoir comment fonctionne quelque chose pour lutiliser : ainsi
on nest pas tent dutiliser des proprits qui nappartiennent pas la structure de donne abstraite
mais seulement une mise en uvre particulire.
Dans ce paragraphe nous nous intressons la mise en uvre au moyen de classes. Ce faisant nous
changeons de casquette : dans les paragraphes prcdents nous avions la casquette utilisateur
des listes, nous coiffons maintenant la casquette ralisateur ou implmenteur des listes.

11.4.1

Reprsentation des lments dune liste


Les listes peuvent tre ralises de plusieurs faons. Les deux faons traditionnelles sont :

base de structures chanes (maillons chans),


base de tableau.
Nous tudions dans ce qui suit la mise en uvre base de maillons chans.Pour chaque lment de
la liste il est cr une structure trois champs, appele un maillon, compose de :

element : la valeur de llment pour lequel ce maillon existe,


suivant : la rfrence au maillon associ llment suivant dans la liste,
precedent : la rfrence au maillon associ llment prcdent dans la liste.
un maillon
element
suivant
precedent

12

maillon de tte

maillon de queue

12
null

12

null

Dans le cas dun maillon associ llment de tte, precedent vaut null (il ny a pas de prcdent) et dans le cas dun maillon associ llment de queue, suivant vaut null (il ny a pas de
suivant). La dfinition de la structure Maillon est :
class Maillon {
int element; Maillon suivant; Maillon precedent;
Maillon(Maillon s, Maillon p, int e) {
element=e; suivant=s; precedent=p;
}
}
La liste est alors reprsente par deux rfrences :

tete : la rfrence au maillon associ llment de tte,


queue : la rfrence au maillon associ llment de queue.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

197

Mise en uvre des listes

La figure suivante illustre la reprsentation de la liste <12, 45, 19, 23> :


les maillons

une liste

12

tete

45

19

23

null

null

queue
La rfrence llment de queue est redondant : on pourrait sen passer et le retrouver en parcourant les maillons partir de la tte. Mais on la conserve pour une meilleure efficacit car elle
permet davoir un accs immdiat la queue de la liste.
Il faut bien sr se mfier du cas particulier de la liste vide. La liste vide est simplement reprsente
par tete=null et queue=null (il ny a aucun lment, donc aucun maillon).
une liste vide
tete

null

queue

null

La dfinition des donnes de la classe ListeDEntiers est donc :


public class ListeDEntiers {
private Maillon tete;
private Maillon queue;
public ListeDEntiers() { // constructeur liste vide
tete=null; queue=null;
}
...
La liste vide est caractrise par tete==null et queue==null . Il suffit de tester lune des deux
galits null, car lune implique lautre (si la programmation de la liste est correcte). Le test
estVide se programme donc simplement ainsi :
public boolean estVide() {return tete==null;}
Un peu plus compliqus sont les ajouts et retraits dun lment. Considrons lajout dun lment
en tte de liste. Il faut distinguer le cas o la liste est vide :
avant

aprs

tete

null

tete

queue

null

queue

nouveau maillon
12
null
null

tete et queue doivent recevoir la rfrence au nouveau maillon cr pour llment rajout.

198

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : listes

et le cas o la liste contient dj des lments :


avant

45

tete
queue
aprs
nouveau maillon
12
tete

exPremier
45

null

queue

La variable tete et le champ precedent de exPremier (le maillon de tte avant lajout) doivent recevoir la rfrence au nouveau maillon. La variable queue reste inchange dans ce cas.
Cette analyse conduit la rdaction suivante pour la mthode ajouteEnTete :
public void ajouteEnTete(int nouvelElement) {
if (tete==null) {
// cas liste vide
tete = new Maillon(null,null,nouvelElement);
queue = tete;
}
else {
// chane en tete
Maillon exPremier = tete;
tete = new Maillon(exPremier,null,nouvelElement);
exPremier.precedent=tete;
}
}
La mthode ajouteEnQueue sobtient par une analyse similaire. Il suffit dchanger les vocables
tete et queue ainsi que precedent et suivant :
public void ajouteEnQueue(int nouvelElement) {
if (queue==null) {
// cas liste vide
queue = new Maillon(null,null,nouvelElement);
tete = queue;
}
else {
// chaine en queue
Maillon exDernier = queue;
queue = new Maillon(null,exDernier,nouvelElement);
exDernier.suivant=queue;
}
}
Pour le retrait de llment de tte, tete reoit la rfrence au maillon suivant de tete. Il faut
galement grer correctement le chanage des prcdents. Dans le cas o la liste ne devient pas

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

199

Mise en uvre des listes

vide, le champ prcdent du nouveau maillon de tte devient null :


avant

futur maillon de tte


12

tete

45

null

queue
aprs

45
null

tete
queue

et si la liste devient vide cest queue qui devient null :


avant
12
null
null

tete
queue

aprs
tete

null

queue

null

Cette analyse conduit la programmation suivante pour la mthode retireEnTete :


public int retireEnTete() {
int resul = tete.element;
tete = tete.suivant;
if (tete!=null) {tete.precedent=null;}
return resul;
}

else {queue = null;}

La programmation de retireEnQueue est similaire :


public void retireEnQueue() {
int resul = queue.element;
queue = queue.precedent;
if (queue!=null) {queue.suivant=null;} else {tete = null;}
return resul;
}

200

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : listes

Remarque sur les erreurs


La tentative de retrait dun lment de la liste vide est considre comme une erreur. Nous navons
pas cherch ici dtecter ces erreurs. Il y a plusieurs raisons cela :

Une raison est de ne pas alourdir le texte des mthodes susceptibles de provoquer une erreur.
Une autre est de considrer que cest lutilisateur de la classe de ne pas provoquer derreur, et
on lui a clairement indiqu dans les spcifications quelles sont les oprations interdites.

Une dernire raison est que, tant donne la mise en uvre, ces erreurs seront dtectes car elles
conduisent tenter daccder un maillon dsign par la rfrence nulle, ce qui provoque une
erreur null pointer exception signale par le langage lexcution.

11.4.2

Parcours de listes

11.4.2.1 Reprsentation dun parcours


Un parcours de liste se fait au moyen dun objet de la classe Parcours . Avec la reprsentation de
la liste par maillons chans, un tat de parcours est simplement la rfrence au maillon associ
llment courant du parcours. Nous appelons courant cette rfrence. La figure suivante illustre
un parcours dont ltat dsigne le deuxime lment de sa liste :
un parcours
courant
sa liste
12
tete

45

19

null

23

null

queue

public class Parcours {


private Maillon courant;

Parcours() {courant=tete;} // Parcours initialis au dbut


Parcours(Parcours
p)
// {Parcours
initialis
avec ltat
courant=p.courant;
}
...

11.4.2.2 Test de fin dun parcours


Ltat estEnFin , tat de parcours qui ne dsigne aucun lment, est naturellement reprsent par
courant=null . Do la programmation du test estEnFin :
public boolean estEnFin() {return courant==null;}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

201

de p

Mise en uvre des listes

11.4.2.3 Mthodes de dplacement dun parcours


Les positionnements en tte et en queue de liste se font simplement en affectant courant respectivement la valeur de tete et de queue de la liste :
public void tete() {courant=tete;}
public void queue() {courant=queue;}

Les positionnements sur llment suivant et prcdent donnent lieu une erreur si le parcours est
en fin. Sinon, il suffit daffecter courant respectivement par la rfrence au maillon suivant ou
prcdent, cest--dire par les valeurs contenues dans les champs suivant ou precedent du
maillon courant :
public void suivant() {courant=courant.suivant;}
public void precedent() {courant=courant.precedent;}

11.4.2.4 Mthodes daccs llment courant


Les accs llment courant, en lecture comme en modification, donnent lieu une erreur si le
parcours est en fin. Sinon, il suffit daccder au champ element du maillon courant :
public int elementCourant() {return courant.element;}
public void modifElement(int nouvelElement) {
courant.element=nouvelElement;
}

11.4.2.5 Mthodes dajout et de retrait dlments


Ajout dun lment
Lajout dun lment est assez compliqu cause des cas particuliers.
Si le parcours est en fin, llment doit tre ajout en tte. Dans ce cas le suivant du nouveau
maillon est le contenu de tete, son prcdent est null et tete doit recevoir la rfrence au nouveau maillon :

202

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : listes

avant

aprs

un parcours
courant

un parcours

null

courant

sa liste

sa liste

tete

suivant du
nouveau

queue

nouveau maillon
12

tete

null

queue

suivant du
nouveau

Si le parcours nest pas en fin, llment doit tre insr la suite de llment courant. Dans ce cas
le suivant du nouveau maillon est le suivant du courant, son prcdent est le maillon courant et le
champ suivant du maillon courant doit recevoir la rfrence au nouveau maillon :
avant

aprs
un parcours

un parcours

courant

courant

nouveau maillon
12

45

45

suivant du
nouveau

suivant du
nouveau

Dautre part, dans tous les cas, le nouvel lment doit devenir llment courant du parcours. Cela
est ralis en affectant courant avec la rfrence au nouveau maillon.

Il reste grer correctement le chanage permettant le passage au prcdent. Dans le cas o le suivant du nouveau maillon nest pas null, le champ precedent du suivant du nouveau maillon
reoit la rfrence au nouveau maillon.
un parcours
courant

nouveau suivant du
maillon nouveau
94
12

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

203

Mise en uvre des listes

Dans le cas o le suivant du nouveau maillon est null, cela signifie que le nouvel lment est llment de queue. Cest donc la variable queue qui reoit la rfrence au nouveau maillon.
un parcours
courant
sa liste
tete

nouveau
maillon
12
null

queue

Cette analyse conduit la programmation suivante :


public void ajouteElement(int nouvelElement) {
Maillon suivantDuNouveau; Maillon nouveau;
if (courant==null) { // chaine en tte
suivantDuNouveau=tete;
nouveau = new Maillon(tete,null,nouvelElement);
tete=nouveau;
}
else { // chaine sur courant
suivantDuNouveau=courant.suivant;
nouveau = new Maillon(suivantDuNouveau,courant,nouvelElement);
courant.suivant=nouveau;
}
courant=nouveau;
if (suivantDuNouveau!=null) {
suivantDuNouveau.precedent=courant;
}
else { // nouvel lment de queue
queue=courant;
}
}
Retrait dun lment
Pour le retrait dun lment, il suffit de grer convenablement les chanages suivant et prcdent.
Pour le chanage suivant, si le prcdent du courant existe, il faut affecter son champ suivant
avec la valeur du champ suivant du courant. Sil nexiste pas, cest--dire sil vaut null, cela
correspond au cas o on retire llment de tte. Le suivant du courant devient la nouvelle tte et
cest donc la variable tete de la liste qui doit recevoir la valeur du champ suivant du courant.
Le principe est similaire pour le chanage prcdent. Si le suivant du courant existe, il faut affecter
son champ precedent avec la valeur du champ precedent du courant. Sil nexiste pas, cest-

204

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : listes

-dire sil vaut null, cela correspond au cas o on retire llment de queue. Le prcdent du courant devient la nouvelle queue et cest donc la variable queue de la liste qui doit recevoir la valeur
du champ precedent du courant (ouf !).
Le parcours doit se positionner sur llment suivant llment supprim (ou en fin de parcours si on
vient de supprimer la queue). Dans tous les cas, cela se fait simplement en affectant la variable
courant du parcours avec la rfrence au suivant de llment supprim.
retrait dans le milieu
avant

le parcours

le parcours
courant

courant

45

aprs

12

94

45

94

retrait en tte
le parcours

avant

le parcours

courant

la liste

avant

courant

12

94

94

la liste

tete

tete

queue

queue

De cette analyse rsulte la programmation suivante. Comme cest souvent le cas, le programme est
plus simple que les explications qui le justifient :
public void retireElement() {
Maillon suivant=courant.suivant;
Maillon precedent=courant.precedent;
if (precedent!=null) {precedent.suivant=suivant;}
else {tete=suivant;}
if (suivant!=null) {suivant.precedent=precedent;}
else {queue=precedent;}
courant=suivant;
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

205

Prcisions sur les classes internes

11.5

Prcisions sur les classes internes


Java permet de dfinir une classe lintrieur dune autre classe. Une telle classe est dite interne
(inner class en anglais). Lintrt des classes internes est de mieux structurer les programmes.
Notamment, le nom dune classe interne est local la classe englobante. Soit une classe B interne
une classe A. Depuis le texte de la classe A, la classe B est dsigne directement par B. Depuis le
texte dune autre classe C, la classe B interne A est dsigne par A.B.
class A {
class B {
}
...
B b;
...
}

class C {
...
A.B b;
...
}

Les vocables private et public sappliquent aux classes internes. Une classe interne public
est utilisable depuis toute classe, alors quune classe interne private nest utilisable que depuis le
texte de la classe englobante. Cela permet de cacher les classes internes qui sont des outils destins
ntre utiliss que par la classe englobante, pour une mise en uvre particulire de labstraction
reprsente par cette classe.
Exemple : la classe Maillon , interne la classe ListeDEntiers , nest quun outil de mise en
uvre des listes. Elle na pas tre propose lextrieur de la classe ListeDEntiers , cest une
notion qui doit rester totalement trangre aux utilisateurs de la classe ListeDEntiers . La
classe Maillon est donc private .
Autre exemple : en revanche, la classe Parcours , galement interne la classe ListeDEntiers, reprsente une notion offerte aux utilisateurs de la classe ListeDEntiers . Cela fait
partie de labstraction liste de pouvoir tre parcourue au moyen dun Parcours . Les utilisateurs
des listes vont explicitement utiliser des objets de type Parcours . La classe Parcours est donc
public .
class ListeDEntiers {
private static class Maillon {
}
public class Parcours {
}
...
Maillon m;
...
}

class UtiliseListes {
...
ListeDEntiers uneListe;
...
ListeDEntiers.Parcours p;
...
}

En Java, il existe deux sortes de classes internes :


Les classes internes statiques, affubles du vocable static : une telle classe nest attache
aucun objet instance courante de la classe englobante. Cest le cas ici de la classe Maillon . La
classe Maillon nest lie aucun objet de type ListeDEntiers .
Les classes internes dynamiques, affubles daucun vocable supplmentaire : une telle classe est
attache un objet instance courante de la classe englobante. Cest le cas ici de la classe Parcours . Un Parcours est toujours cr propos dun objet ListeDEntiers particulier, sa
liste. Depuis le texte dune telle classe, on peut citer directement les donnes et mthodes membres

206

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : listes

de lobjet de la classe englobante propos duquel lobjet de la classe interne a t cr (on lappelle
instance courante de la classe englobante). Par exemple, depuis la classe Parcours , on utilise
tete et queue : ces identificateurs dsignent directement les variables tete et queue de lobjet
de type ListeDEntiers auquel cet objet de type Parcours est attach.
Une image que lon peut donner de cette notion de classe interne dynamique est celle dune usine
fabriquer des objets rattache chaque objet de la classe englobante. Ainsi, chaque objet de type
ListeDEntiers possde une usine fabriquer des parcours sur cette liste.
liste A

liste B

parcours 1 de liste A
parcours 2 de liste A

parcours 1 de liste B
parcours 2 de liste B
parcours 3 de liste B

Pour des raisons de structuration, il est prfrable que les objets de la classe interne soient crs par
des mthodes de la classe englobante. Cest ce que nous avons fait pour les parcours de listes : la
classe ListeDEntiers possde les mthodes nouveauParcours qui rendent en rsultat un
nouvel objet de type Parcours oprant sur la liste laquelle on applique la mthode. On peut
appeler ces mthodes des fabricateurs. Elle encapsulent des appels aux vrais constructeurs, pour
des raisons de modularit et de contrle lors de la cration (ici il ny a pas de contrle, mais il pourrait y en avoir) :
public class ListeDEntiers {
...
public Parcours nouveauParcours() {
// un nouveau parcours initialis au dbut de la liste
return new Parcours();
}
public Parcours nouveauParcours(Parcours p) {
// nouveau parcours initialis ltat de parcours de p
return new Parcours(p);
}
}

...

La premire mthode, la plus utilise, cre un parcours positionn sur le dbut de la liste (ou dans
ltat estEnFin si la liste est vide).
La deuxime mthode cre un parcours positionn dans le mme tat quun autre parcours p pass
en paramtre. Cela peut tre utile pour effectuer certaine recherches complexes dans une liste.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

207

Prcisions sur les classes internes

Exercice 11.1

Manipulations de listes

Faire tourner sur un schma chacune des procdures suivantes et indiquer ce quelles font.
import list.*;
class ManipulationDeListes {
static ListeDEntiers carres(int j) {
ListeDEntiers c = new ListeDEntiers();
for(int i=1;i<=j;i++){c.ajouteEnQueue(i*i);}
return c;
}
static int p1(int j) {
ListeDEntiers c = carres(9);
ListeDEntiers.Parcours
p= c.nouveauParcours();
p.queue();
while(!p.estEnFin()){
int i=p.elementCourant();
if (i%j==0){ return i;}
else { p.precedent();}
}
return 0;
}
static ListeDEntiers p2(){
ListeDEntiers l = carres(9);
ListeDEntiers.Parcours
p= l.nouveauParcours();
while(!p.estEnFin()){
int i=p.elementCourant();
if (i%3==0) { p.modifElement(i+1);}
p.suivant();
}
return l;
}
static ListeDEntiers p3(){
ListeDEntiers resul = carres(9);
ListeDEntiers.Parcours
p= resul.nouveauParcours();
p.queue();
while(!p.estEnFin()){
int i=p.elementCourant();
if (i%4==0) { p.retireElement();}
p.precedent();
}
return resul;
}
}

208

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : listes

Exercice 11.2

Fusion de deux listes tries

Soient L1 et L2 deux listes dentiers, tries par ordre croissant de la tte la queue.
On veut construire la liste L qui contient tous les lments de L1 et L2 trie par ordre croissant.
exemple :L1 = 0 1 3 5 12

L2 = 1 2 5 13 15 16

L = 0 1 1 2 3 5 5 12 13 15 16
Rdiger une fonction qui rend en rsultat la liste L ainsi construite.
Exercice 11.3

Usage des rfrences : arbre gnalogique

Afin de construire des arbres gnalogiques, on se propose de rdiger la classe Personne qui
satisfait aux spcifications suivantes :
Une personne possde un nom, un pre, une mre et des enfants. Lors de la cration dune
personne on indique son nom, son pre et sa mre. La liste de ses enfants est initialise vide et
cette personne est rajoute dans la liste des enfants de son pre et de sa mre.
nom
Adam
pre ?
mre ?
enfants

nom
Abel
pre .
mre .
enfants

nom
Eve
pre ?
mre ?
enfants

nom
Cain
pre .
mre .
enfants

nom
Ida
pre .
mre .
enfants

On dispose de la classe ListeDePersonnes , dote des mmes fonctionnalits que ListeDEntiers (sauf que les lements sont des refrences des objets de type Personne )
Rdiger la classe Personne et un programme principal de test qui cre la population illustre sur
la figure, puis imprime la liste des enfants de la grand-mre paternelle de Ida.

Exercice 11.4

Compression - dcompression

Certaines applications utilisent des suites de donnes dans lesquelles de nombreuses valeurs successives sont identiques. Cest par exemple le cas pour reprsenter des images formes de grandes plages de mme couleur. Une telle suite de valeurs entires est reprsente sous forme compresse de
la faon suivante : chaque sous-suite de k lments conscutifs gaux de valeur e est reprsente
dans la liste compresse par k suivi de e. Exemple :
La compression de la suite L=<6, 6, 6, 6, 6, 2, 3, 3, 3, 3, 1, 1> est la suite <5, 6, 1, 2, 4, 3, 2, 1> car
L contient cinq 6 suivi de un 2 suivi de quatre 3 suivi de deux 1.
Rdiger la fonction compression qui rend en rsultat la compression dune liste dentiers passe
en paramtre. Le rsultat devra tre une nouvelle liste cre par la fonction.
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

209

Prcisions sur les classes internes

La dcompression, opration inverse de la compression, consiste reconstituer la liste des valeurs


reprsentes par une liste compresse. Exemple :
La dcompression de la liste L=<5, 6, 1, 2, 4, 3, 2, 1> est la liste <6, 6, 6, 6, 6, 2, 3, 3, 3, 3, 1, 1>.
Rdiger la fonction deCompression qui rend en rsultat la dcompression dune liste dentiers
passe en paramtre.

Exercice 11.5

Ordre alphabtique dune population

On suppose faites les dclarations et crations suivantes :


class Individu {
public String nom; public int age;
public Individu(String n, int a) {
nom=n; age=a
}
}
class TestPopulation {
static Individu[]
population = new Individu[1000];
static int nombreDIndividus = 0;
static ListeDEntiers L = new ListeDEntiers();
...
}
nombreDIndividus doit indiquer le nombre dindividus introduits dans le tableau population.
Chaque nouvel individu est introduit dans le tableau population , la suite des prcdents.
La liste L sert indiquer lordre alphabtique des individus entrs dans le tableau population .
Chaque lment de la liste contient lindice dun individu dans le tableau population et cette
liste est en permanence trie selon lordre alphabtique des noms des individus.
Exemple :
population
0
1
2
3
4

Renault

54

nombreDindividus

Leblanc

42

Martin

12

Piron

86

Durand

76

Rdiger la procdure afficheParOrdreAlphabetique qui affiche les noms des individus


contenus dans le tableau population par ordre alphabtique.
Rdiger la procdure ajouteIndividu qui introduit un nouvel individu dans le tableau population et met jour nombreDIndividus et la liste L de telle sorte quelle indique toujours
lordre alphabtique des lments du tableau population .

210

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Utilisation des fichiers

CHAPITRE 12

Utilisation des fichiers

Un fichierest une collection de donnes stocke de faon persistante sur un support priphrique
(disque, disquette, bande magntique, CDROM, etc...). Sur un mme support on peut mettre plusieurs fichiers, do la ncessit de nommer un fichier et parfois son support galement.
L'utilisation des fichiers rpond trois besoins principaux :

conservation persistante dinformation, alors que la mmoire est en gnral volatile,


stockage de grandes quantits dinformations qui ne tiendraient pas en mmoire centrale,
transport dinformation dun ordinateur un autre.
Les exemples dutilisation de fichiers sont nombreuses :

Les relevs pluviomtriques ou de tempratures d'une rgion sont stocks sur fichiers, ce qui

permet de faire quand on le dsire certaines statistiques.


Une banque doit lors de chaque transaction, mettre jour un compte client. Ceci impose que la
banque possde de faon permanente les renseignements concernant chacun de ses clients :
nom, prnom, adresse, numro de compte, tat du compte...
Plus proche des applications grand public, on trouve tout ce qui concerne le stockage de documents de traitement de textes ou de photos.

La structure des fichiers est trs spcifique chaque langage de programmation. Nous prsentons
ici uniquement les fichiers squentiels de texte.

12.1

Fichiers squentiels de texte


Un fichier squentielcontient une suite dlments. Laccs est squentiel, ce qui signifie que
laccs au nime lment ne peut se faire quaprs avoir accd aux n-1 lments prcdents. Ceci
soppose aux fichiers accs direct o lon peut accder directement au nime lment.
Un fichier squentiel de texte contient simplement une suite de caractres. Il prsente lavantage
dtre gnralement peu prs compatible avec tous les langages de programmation, tout simplement parce que la reprsentation des caractres est normalise (codages ASCII sur 7 et 8 bits et plus
rcemment codage UNICODE sur 16 bits). Les autres fichiers squentiels sappellent fichiers squentiels binaires1 : ils contiennent des donnes de nimporte quel type, mais ces donnes tant
codes selon une convention propre chaque langage de programmation, ils ne sont utilisables que
par des programmes rdigs dans le langage qui les a produit.

1. Le terme binaire est mal choisi, car toute information, en mmoire ou sur fichier, est reprsente par des successions de
bits. Un terme plus juste serait codage brut ou spcifique, qui ne passe pas par lintermdiaire de caractres pour
reprsenter linformation.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

211

Utilisation de fichiers textes en Java

Bien que compos physiquement dune suite de caractres, un fichier texte peut contenir tous types
dinformation. Il suffit davoir une convention de reprsentation des divers types de donnes sous
forme de chanes de caractres et de disposer des fonctions de conversion correspondantes. Pour les
types de base, on utilise les conventions naturelles dj pratiques pour les lectures clavier et les
critures cran : notation dcimale pour les entiers, notation avec point et exposant pour les rels,
true et false pour les boolens...
Un avantage des fichiers textes, en plus de la compatibilit avec tous les langages de programmation, est la lisibilit humaine des informations. Les informations sont crites en clair, sous
forme de chanes de caractres que lon peut lire ou construire avec tout diteur de texte disponible
sur tout ordinateur. Ainsi, moyennant la connaissance de certaines conventions propres la gamme
dapplication manipulant ces fichiers, ils sont comprhensibles par un lecteur humain.
Par exemple, un fichier contenant les noms et les ges dune population se prsentera ainsi :
toto 12 jules 14 alfred 18 marcel 9
et pour le lire il suffira dexcuter, comme pour une entre depuis le clavier, les procdures spcifiques de lecture suivantes :
...lireChaine()... lit le nom "toto" sous forme dune donne de type String
puis ...lireUnEntier()... lit lge 12 sous forme dune donne de type int
puis ...lireChaine()... lit le nom "jules" sous forme dune donne de type String
puis ... lireUnEntier()

lit...
lge 14 sous forme dune donne de type int

etc...

12.2

Utilisation de fichiers textes en Java


Nous prsentons ici un moyen relativement simple dutiliser les fichiers squentiels de texte en
Java. On propose deux classes :

la classe LectureFichierTexte pour lire des fichiers textes,


et la classe EcritureFichierTexte pour crire des fichiers textes.
Ces deux classes, comme la classe Lecture que nous avons utilise pour les entres clavier, ne
font pas partie de la bibliothque standard de Java. La bibliothque standard, java.io , est riche
en possibilits mais est sans doute un peu trop technique pour les besoins courants.

12.2.1

Lecture de fichiers textes


Pour lire un fichier texte, on utilise un objet de type LectureFichierTexte . Pour crer un tel
objet, il suffit dexcuter :
LectureFichierTexte population
= new LectureFichierTexte("lesEnfants.txt");
Ceci cre un accs en lecture au fichier appel lesEnfants.txt suppos exister dans la
machine (dans le rpertoire courant dans ce cas, mais on peut indiquer un chemin relatif ou absolu).
Cette instruction provoque une erreur si ce fichier nexiste pas. Dans cet exemple lobjet qui sert
daccs a t baptis population .
Ensuite, on peut lire le fichier au moyen des mthodes offertes par cette classe. Ces mthodes sont
similaires celles utilises pour les lectures clavier :

212

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Utilisation des fichiers

public class LectureFichierTexte {


public LectureFichierTexte(String nom)
accs en lecture au fichier indiqu par nom, erreur si le fichier nexiste pas
public void fermer()
ferme le fichier
public char lireUnCar()
lecture dun caractre, erreur si lecture impossible
public String lireChaine(String delimiteurs)
lecture dune chane, les dlimiteurs tant les caractres de delimiteurs,
rend la chane vide si fin de fichier, erreur si lecture impossible
public String lireChaine()
lecture dune chane comprise entre dlimiteurs,
les dlimiteurs tant' ' (espace) '\r' (fin ligne Microsoft) ou'\n' (fin ligne Unix)
rend la chane vide si fin de fichier, erreur si lecture impossible
public int lireUnEntier()
lecture dun entier reprsent en dcimal, erreur si lecture impossible ou format incorrect
public double lireUnReel()
lecture dun nombre rel, erreur si lecture impossible ou format incorrect

public boolean finDeFichier()


indique si fin de fichier

La mthode finDeFichier permet de savoir si on est en fin de fichier, cest--dire si tous les
caractres ont t lus. Le rsultat de cette mthode sert souvent dargument pour terminer une boucle qui lit le fichier.
La mthode fermer clt laccs au fichier. Une fois ferm, cest une erreur que de chercher
accder au fichier par cet accs. Une programmation saine ferme les fichiers avant de terminer
lexcution du programme, bien que a marche souvent si on ne le fait pas pour un accs en lecture.
La lecture du fichier donn en exemple pourrait tre ralis par la boucle suivante :
LectureFichierTexte population
= new LectureFichierTexte("lesEnfants.txt");
...
while (!population.finDeFichier()) {
String nom = population.lireChaine();
int age = population.lireUnEntier();
... traitement des donnes lues nom et age ...
}
population.fermer();
...

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

213

Utilisation de fichiers textes en Java

12.2.2

criture de fichiers textes


Pour crire dans un fichier texte, on utilise un objet de type EcritureFichierTexte . Pour
crer un tel objet, il suffit dexcuter :
EcritureFichierTexte population
= new EcritureFichierTexte("lesEnfants.txt");
Ceci cre un accs en criture au fichier appel lesEnfants.txt . Le fichier nexiste pas ncessairement. Sil existe, il sera effac et rcrit par le programme. Sil nexiste pas, il sera cr par
cette instruction.
Ensuite, on peut crire dans le fichier au moyen des mthodes offertes par cette classe. Comme pour
les critures sur cran, il existe une mthode dcriture pour tous les types de base :
public class EcritureFichierTexte {
public EcritureFichierTexte(String nom)
public
public
public
public
public
}

void
void
void
void
void

ecrire(char c)
ecrire(String s)
ecrire(int k)
ecrire(boolean b)
ecrire(double x)

public void fermer()

La mthode fermer clt laccs au fichier. Lopration fermer est ici essentielle : si on ne lexcute pas, les informations ne sont pas conserves dans le fichier.
Voici, titre dexemple, un programme qui cre un fichier appel lesEntiers.txt contenant
les nombres entiers de 0 39 avec, pour chacun deux, lindication sil est pair ou non. Aprs excution, le fichier contiendra :
0 est pair
1 est pair
2 est pair
...
38 est pair
39 est pair

: true
: false
: true
: true
: false

class TestEcriture {
public static void main(String[] arg) {
EcritureFichierTexte sortie =
new EcritureFichierTexte("lesEntiers.txt");
for (int i=0; i<40; i++) {
sortie.ecrire(i);
sortie.ecrire(" est pair
: ");
sortie.ecrire((i%2)==0);
sortie.ecrire('\n'); passage la ligne par criture explicite de '\n'
}
sortie.fermer();
}
}

214

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Utilisation des fichiers

Exercice 12.1

Cration dun fichier de nombres au carr

Rdiger un programme qui lit un fichier entree contenant des entiers et crit dans un fichier sortie
les carrs des valeurs lues.
Le nom (ou le chemin) du fichier entree sera donn au clavier et le nom du fichier sortie sera fabriqu en ajoutant au nom du fichier dentre le suffixe ".carre ".

Exercice 12.2

Dcodage des accents

Il est courant de recevoir des messages o les caractres accentus ont t saisis de la faon
suivante :
Bonjour He'le`ne,
vas-tu e^tre disponible pour aller to^t sur l'i^le de Bre'hat,
ou` il fait beau a` Noel

On souhaite raliser un programme pour transcrire ces messages sous la forme plus lisible :
Bonjour Hlne,
vas-tu tre disponible pour aller tt sur l'le de Brhat,
o il fait beau Nol

Les accentuations possibles sont a avec (, ^), e avec (, , ^, ), i avec (^, ), o avec (^, ) et u
avec (,^, ).
Remarque : on ne cherche traiter ni les majuscules accentues ni le caractre .
Le texte original sera contenu dans un fichier d'entre et le rsultat sera
crit dans un fichier de sortie.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

215

Utilisation de fichiers textes en Java

216

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : ensembles

CHAPITRE 13

13.1

Structures de donnes : ensembles

Spcification du type ensemble


Un ensemble est une collection de donnes dans laquelle toutes les donnes ont une valeur diffrente. Ajouter un lment qui sy trouve dj ne change pas lensemble. Cette structure de donnes
est donc trs proche de la notion mathmatique densemble.
La notion densemble exige que le type des lments soit dot de la notion dgalit. Ce ntait pas
le cas pour des structures telles que le vecteur ou la liste, mais cela est obligatoire ici pour donner
un sens aux oprations telles que le test dappartenance, lunion, lintersection...
La spcification dun type ensemble exige gnralement que le type des lments possde une relation dordre, suprieur ou gal, afin de pouvoir comparer et ordonner les lments. Moins fondamentale que le besoin dgalit, cette exigence permet :

de parcourir un ensemble par ordre croissant de ses lments,


doffrir un test dappartenance et des oprations ensemblistes trs performantes : ceci concerne

la mise en uvre, mais sans relation dordre sur les lments, la recherche dun lment est
ncessairement proportionnelle la taille de lensemble (en moyenne), alors quil est proportionnel au logarithme de la taille si on opre sur une collection trie (voir les exercices concernant la recherche par dichotomie). Les oprations ensemblistes dunion, dintersection et de
diffrence prennent un temps proportionnel au produit des tailles des ensembles oprandes dans
le cas densembles non tris, alors quil ne prennent quun temps proportionnel la taille des
oprandes si les ensembles sont tris. Ces rapports de performance sont considrables ds que la
taille des ensembles devient importante.

Nous prsentons ici des ensembles dentiers, raliss par la classe EnsembleDEntiers . Bien
videmment on peut imaginer les mmes fonctionnalits pour des ensembles de nimporte quel type
dlments, pourvu que ce type soit dot des oprations de comparaison dgalit et de supriorit.
La classe EnsembleDEntiers ralise des ensembles modifiables: ce sont des objets dont ltat
est un ensemble fini, ventuellement vide, de valeurs entires. On peut le faire voluer en taille en
ajoutant ou en retirant un lment.
Comme pour les listes, cette classe offre une classe interne EnsembleDEntiers.Parcours
qui permet de crer des objets pour parcourir un ensemble par ordre croissant de ses lments.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

217

Spcification du type ensemble

La spcification de la classe EnsembleDEntiers est :

public class EnsembleDEntiers {


public EnsembleDEntiers()
public boolean estVide()
public int cardinal()

// constructeur
// rsultat

// rsultat

: ensemble vide

: indique si this est vide


: nombre dlments de this

public boolean contient(int e)


// rsultat
: indique si e appartient this
public void ajouteElement(int e)
// effet
: ajoute e this (aucun effet si e est dj dans this)
public void retireElement(int e)
// effet : retire
e d e this

(aucun

effet

si e nest

pas

dans

th

public static EnsembleDEntiers


union(EnsembleDEntiers e1, EnsembleDEntiers e2)
// rsultat
: un nouvel ensemble gal lunion de e1 et e2
public static EnsembleDEntiers
intersection(EnsembleDEntiers e1, EnsembleDEntiers e2)
// rsultat
: u n nouvel
ensemble
gal lintersection

de e1

public static EnsembleDEntiers


difference(EnsembleDEntiers e1, EnsembleDEntiers e2)
// rsultat
: un nouvel ensemble gal la diffrence de e1 et e2
public String toString()
// rsultat
: une reprsentation en clair de this, sous la forme
//
{ 12 | 23 | 45 | 72}, {} pour lensemble vide
public Parcours nouveauParcours()
// rsultat
: un nouveau parcours initialis sur le plus petit
// lment de this ou en fin si this est vide
public Parcours nouveauParcours(EnsembleDEntiers.Parcours p)
// rsultat
: un nouveau parcours initialis ltat de p

218

//================================================================
public class Parcours {
public void tete()
// effet
: positionne this sur le plus petit lment
public void suivant()
// effet
: positionne this sur lelment suivant
public boolean
estEnFin()
// rsultat
: indique
si this est termin
public int elementCourant() // prrequis
: this nest pas en fin
// rsultat
: lment courant de this
public void retireElement() // prrequis
: this nest pas en fin
// effet
: retire llment courant de this
} //==============================================================

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : ensembles

13.2

Exemples dutilisation densembles


Pour parcourir un ensemble par ordre croissant de ses lments, on procde de la faon suivante :
EnsembleDEntiers ens; ...
EnsembleDEntiers.Parcours p = ens.nouveauParcours();
while(!p.estEnFin()) {
... exploiter p.elementCourant()... p.suivant();
}
EnsembleDEntiers ens

12

25

suivant
debut
ou cration

32

74
estEnFin

EnsembleDEntiers.Parcours p
On peut prendre comme exemple dapplication la rdaction dune fonction inclus qui indique si
un ensemble e1 est inclus dans un ensemble e2. Lalgorithme de cette fonction consiste parcourir
lensemble e1 et vrifier que chacun de ses lments appartient e2 :
static boolean inclus(EnsembleDEntiers e1, EnsembleDEntiers e2) {
EnsembleDEntiers.Parcours p = e1.nouveauParcours();
boolean estInclus=true;
while(!p.estEnFin() && estInclus) {
if(!e2.contient(p.elementCourant())){estInclus=false;}
p.suivant();
}
return estInclus;
}

13.3

Mise en uvre des ensembles


Il y a de nombreuses faons de reprsenter un ensemble.
On peut utiliser simplement une liste telle que celle spcifie dans un chapitre prcdent, mais dans
ce cas le test dappartenance ncessite un temps proportionnel (en moyenne) la taille de lensemble car la recherche dans la liste ne peut se faire que par un parcours squentiel.
Nous allons dcrire ici une ralisation au moyen de tableaux dont la taille sadapte la taille de
lensemble. Le tableau contient les lments de lensemble tris par ordre croissant. Ainsi la recherche dun lment peut tre ralise en un temps proportionnel au logarithme de la taille de lensemble, grce une recherche dichotomique.
Une variable entire nbElements contient tout moment la taille effective de lensemble.
Lensemble vide est reprsent par nbElements=0 et aucun tableau (on aurait pu utiliser un
tableau de taille nulle, mais aucun tableau occupe encore moins de place) :
public class EnsembleDEntiers {
private int nbElements;
private int[] elements; tableau de taille ajuste aux besoins
public EnsembleDEntiers() { ensemble vide
elements=null; nbElements=0;
} ...

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

219

Mise en uvre des ensembles

La fonction estVide rend simplement vrai si et seulement si nbElements==0 .


public boolean estVide() {return nbElements==0;}
La fonction cardinal rend simplement nbElements .
public int cardinal() {return nbElements;}
Le tableau elements contient tout moment les lments de lensemble, tris par ordre croissant,
dans les positions dindice 0 nbElements-1 .
La fonction suivante recherche un lment par dichotomie. Elle rend en rsultat lindice de llment cherch sil est prsent et -1 sil est absent. Cest une fonction prive (non offerte aux utilisateurs, car elle est spcifique la reprsentation par tableau et na aucun sens au niveau de
lensemble).
private int indiceDe(int e) {
// rsultat : indice de llment gal e, 0..nbElements-1,
// -1 si e est absent
int i = 0; int j = nbElements-1;
while(j>=i){
int m=(i+j)/2;
if (elements[m]==e) {return m;}
else if (elements[m]<e) {i=m+1;}
else {j=m-1;}
}
return -1;
}
Mthode contient
Cette fonction est utilise pour raliser la fonction contient , offerte aux utilisateurs, qui sert
tester si lensemble contient un lment :
public boolean contient(int e) {
// indique si e appartient this
return indiceDe(e)!=-1;
}
Mthode ajouteElement
Lajout dun lment lensemble est assez complexe. Il faut dabord tester si llment appartient
lensemble. Dans ce cas il ny a rien faire.
Ensuite il faut vrifier sil y a assez de place dans le tableau pour rajouter un lment. Sinon, il faut
crer un nouveau tableau 2 fois plus grand (ou de taille 1 si lensemble tait vide). Afin de pouvoir
modifier les aspects quantitatifs, on a utilis une constante facteurDeCroissance pour le facteur de croissance (gal 2 ici) et une constante taillePourSingleton pour la taille du
tableau qui reprsente un ensemble un seul lment (gale 1 ici).
private static final int facteurDeCroissance=2;
private static final int taillePourSingleton=1;
Dans les deux cas il faut insrer le nouvel lment sa place dans le tableau, de faon le conserver
tri par ordre croissant.

220

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : ensembles

Remarque : la reprsentation au moyen dun tableau nest pas efficace en ce qui concerne lajout et
le retrait dlments : linsertion pour lajout et le tassement pour le retrait sont des oprations qui
ncessitent en moyenne un temps proportionnel la taille de lensemble. Une reprsentation plus
efficace utilise un arbre binaire ordonn et quilibr, appel arbre AVL, dont la mise en uvre
sort du cadre de ce cours.
place suffisante
ajouteElement(9)
elements
nbElements

4
26
53

elements
nbElements

4
9
26
53

place insuffisante
ajouteElement(12)
elements
nbElements

4
9
26
53

elements
nbElements

4
9
26
53

4
9
12
26
53

public void ajouteElement(int e) {


// ajoute e this (aucun effet si e est dj dans this)
if (!contient(e)) {
if (elements!=null && nbElements<elements.length){
// place suffisante
int i=nbElements;
while (i>0 && elements[i-1]>e) {
elements[i]=elements[i-1]; i--;
}
elements[i]=e;
}
else { // place insuffisante, cration dun nouveau tableau
int[] nouveau;
if (nbElements==0) {
nouveau = new int[taillePourSingleton];
}
else {
nouveau=new int[facteurDeCroissance*elements.length];
}
int i=nbElements;
while (i>0 && elements[i-1]>e) {
nouveau[i]=elements[i-1]; i--;
}
nouveau[i] = e; i--;
while (i>=0) { nouveau[i]=elements[i]; i--;}
elements=nouveau;
}
nbElements++;
}
}
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

221

Mise en uvre des ensembles

Mthode retireElement
Le retrait dun lment prsente des difficults similaires. Il faut commencer par tester si llment
appartient lensemble, sinon il ny a rien faire. Le test dappartenance ayant donn lindice de
llment retirer, on utilise cet indice en appelant une procdure prive retireIemeElement
qui retire le ime lment.
public void retireElement(int e) {
// retire e de this (aucun effet si e nest pas dans this)
int k = indiceDe(e);
if (k!=-1) {retireIemeElement(k);}
}
Lorsque le nombre dlments devient plus petit que la moiti de la taille du tableau, un nouveau
tableau plus petit est cr de faon ne pas encombrer inutilement la mmoire. Lancien tableau,
sauf llment retir, est recopi dans le nouveau. Dans le cas o un tableau plus petit nest pas
recr, le tableau est simplement tass dune position partir de lindice de llment supprim. Il
y a un cas particulier lorsque lensemble devient vide, car on a dcid de reprsenter lensemble
vide par aucun tableau. Dans ce cas, la rfrence au tableau est mise null.
cas de cration dun tableau plus petit
retireElement(12)
elements
nbElements

4
9
12
26
53

elements
nbElements

4
9
26
53

4
9
12
26
53

cas de simple tassement


retireElement(9)
elements
nbElements

4
9
26
53

elements
nbElements

4
26
53

private void retireIemeElement(int k) {


// retire le k ime lment
nbElements--;
if (nbElements==0) {elements = null;}
else if (nbElements < elements.length/facteurDeCroissance) {
// cration dun tableau plus petit
int[] nouveau =
new int[elements.length/facteurDeCroissance];
for (int i=0;i<k; i++) {nouveau[i]=elements[i];}
for (int i=k;i<nbElements; i++) {nouveau[i]=elements[i+1];}
elements=nouveau;
}
else {
for (int i=k;i<nbElements;
i++) {elements[i]=elements[i+1];}
}
}

222

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : ensembles

Oprations ensemblistes
La ralisation des oprations ensemblistes, union, intersection et diffrence, profite du fait que les
tableaux sont tris par ordre croissant. Puisque lon parcourt les lments des ensembles oprandes
par ordre croissant, on gnre les lments du rsultat galement par ordre croissant. Chaque lment du rsultat est donc ajout au bout du tableau, la suite de ceux dj prsents. On utilise
pour cela une procdure outil ajouteAuBout qui ajoute un lment au bout du tableau reprsentant le rsultat. Cette procdure double la taille du tableau chaque fois que cest ncessaire.
private void ajouteAuBout(int e) {
if (elements!=null && nbElements<elements.length){
// place suffisante
elements[nbElements]=e;
}
else if (nbElements==0) {
elements= new int[taillePourSingleton]; elements[0]=e;
}
else {
// place insuffisante, cration dun nouveau tableau
int[] nouveau
= new int[facteurDeCroissance*elements.length];
for (int i=0;i<nbElements;i++) {nouveau[i]=elements[i];}
nouveau[nbElements] = e;
elements=nouveau;
}
nbElements++;
}
Nous avons choisi de raliser les oprations ensemblistes sous forme de fonctions pures, et non pas
par modification dun des oprandes. Le rsultat est donc un nouvel ensemble cr par lopration.
Fonction union
Lunion est ralise comme le montre lexemple suivant :
e1 1 2 3 5 6
e2 2 4 5
union 1

e1 1 2 3 5 6
e2 2 4 5
union 1 2

e1 1 2 3 5 6
e2 2 4 5
union 1 2 3

e1 1 2 3 5 6
e2 2 4 5
union 1 2 3 4

e1 1 2 3 5 6
e2 2 4 5
union 1 2 3 4 5

e1 1 2 3 5 6
e2 2 4 5
union 1 2 3 4 5 6

public static EnsembleDEntiers


union(EnsembleDEntiers e1, EnsembleDEntiers e2) {
EnsembleDEntiers resul=new EnsembleDEntiers(); int i1=0; int i2=0;
while (i1<e1.nbElements && i2<e2.nbElements) {
int v1 = e1.elements[i1]; int v2 = e2.elements[i2];
if (v1<v2) {resul.ajouteAuBout(v1); i1++;}
else if (v1>v2) {resul.ajouteAuBout(v2); i2++;}
else {resul.ajouteAuBout(v1); i1++; i2++;}
}
while(i1<e1.nbElements){resul.ajouteAuBout(e1.elements[i1]);
while(i2<e2.nbElements){resul.ajouteAuBout(e2.elements[i2]);
return resul;
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

223

Mise en uvre des ensembles

Fonctions intersection et difference


Lintersection et la diffrence sont ralises de faon similaire :
e1 1 2 3 5 6
e2 2 4 5
inter.

e1 1 2 3 5 6
e2 2 4 5
inter. 2

e1 1 2 3 5 6
e2 2 4 5
inter. 2

e1 1 2 3 5 6
e2 2 4 5
inter. 2

e1 1 2 3 5 6
e2 2 4 5
inter. 2 5

e1 1 2 3 5 6
e2 2 4 5
inter. 2 5

public static EnsembleDEntiers


intersection(EnsembleDEntiers e1, EnsembleDEntiers e2){
EnsembleDEntiers resul=new EnsembleDEntiers();
int i1=0; int i2=0;
while (i1<e1.nbElements && i2<e2.nbElements) {
int v1 = e1.elements[i1]; int v2 = e2.elements[i2];
if (v1<v2) {i1++;}
else if (v1>v2) {i2++;}
else {resul.ajouteAuBout(v1); i1++; i2++;}
}
return resul;
}

e1 1 2 3 5 6
e2 2 4 5
dif. 1

e1 1 2 3 5 6
e2 2 4 5
dif. 1

e1 1 2 3 5 6
e2 2 4 5
dif. 1 3

e1 1 2 3 5 6
e2 2 4 5
dif. 1 3

e1 1 2 3 5 6
e2 2 4 5
dif. 1 3

e1 1 2 3 5 6
e2 2 4 5
dif. 1 3 6

public static EnsembleDEntiers


difference(EnsembleDEntiers e1, EnsembleDEntiers e2) {
EnsembleDEntiers resul=new EnsembleDEntiers();
int i1=0; int i2=0;
while (i1<e1.nbElements && i2<e2.nbElements) {
int v1 = e1.elements[i1]; int v2 = e2.elements[i2];
if (v1<v2) {resul.ajouteAuBout(v1); i1++;}
else if (v1>v2) {i2++;}
else {i1++; i2++;}
}
while (i1<e1.nbElements) {
resul.ajouteAuBout(e1.elements[i1]); i1++;
}
return resul;
}

224

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : ensembles

Mthode toString :
public String toString() { //reprsentation en clair
if (estVide()) {return "{}";}
StringBuffer resul = new StringBuffer("{| "+ elements[0]);
for (int i=1; i<nbElements; i++) {
resul.append(" , " + elements[i]);
}
resul.append(" |}"); return resul.toString();
}
Mthodes de cration de parcours :
public Parcours nouveauParcours() {
// rsultat
: nouveau parcours initialis en dbut densemble
return new Parcours();
}
public Parcours nouveauParcours(Parcours p) {
// rsultat
: nouveau parcours initialis ltat de p
return new Parcours(p);
}

Mise en uvre des parcours


Un Parcours est simplement reprsent par un nombre entier, indice dans le tableau des lments
de llment couramment dsign.
public class Parcours {
private int courant;
Parcours() {courant=0;}
Parcours(Parcours p) {courant=p.courant;}

public
public
public
public
public

void tete() {courant=0;}


void suivant() {if (courant<nbElements) {courant++;}}
boolean estEnFin() {return courant==nbElements;}
void retireElement() {retireIemeElement(courant);}
int elementCourant() {return elements[courant];}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

225

Mise en uvre des ensembles

Exercice 13.1

Manipulation densembles

Rdiger les fonctions suivantes :


La fonction egalite qui dtermine si deux ensembles sont gaux (contiennent les mmes lments).
static boolean egalite (EnsembleDEntiers E, EnsembleDEntiers F)
//rsultat
: indique si E et F sont gaux
La fonction comprisEntre , qui tant donns un ensemble E et deux entiers a et b, rend un
ensemble contenant les lements de E qui sont compris entre a et b.
static EnsembleDEntiers
comprisEntre(EnsembleDEntiers
// rsultat : ensemble dentiers form des
// lments de E qui sont compris entre a et b

Exercice 13.2

E,int

Reprsentation des Polynmes

On se propose de reprsenter les polynmes coefficients entiers. Afin de reprsenter de faon conomique les polynmes de degr important mais dont la plupart des coefficients sont nuls, on
dcide de raliser une mise en uvre de la classe Polynome au moyen dune liste de paires
dentiers <ci,di> o ci est le ime coefficient non nul par ordre croissant des degrs et di est le degr
correspondant.
Exemple : le polynme 6x45 + 33x12 + 5x + 1
sera reprsent par la liste de paires dentiers : <1,0> <5,1> <33,12> <6,45>
Le polynme nul sera naturellement reprsent par une liste vide.
La classe Polynome offre les fonctionnalits suivantes :
static Polynome zero
le polynme nul
static Polynome aPartirDe(int[] tab)
polynme indiqu par un tableau contenant alternativement les coefficients non nuls et les degrs
correspondants, par ordre dcroissant des degrs, tab = {cn,dn,...,c1,d1,c0,d0}. Ce constructeur
sera essentiellement destin programmer des test. Ainsi avec
int[] tab = {6,45,33,12,5,1,1,0};
Polynome.aPartirDe(tab)
rendra le polynme 6x45 + 33x12 + 5x + 1.
String toString()
forme textuelle en clair du polynme :
an*X^n +... + a2*X^2 + a1*X + a0 ( 0 si this est le polynome nul)

226

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

a,int

Structures de donnes : ensembles

double valeur (double x)


valeur du polynme this au point x
int degre()
degr du polynme. Le degr du polynme nul (-infini en math) sera reprsent par -1
static boolean egalite(Polynome x, Polynome y)
indique si x et y sont gaux
static Polynome somme(Polynome x, Polynome y)
somme de x et de y
static Polynome produit(Polynome x, Polynome y)
produit de x par y
static Polynome derive(Polynome x)
driv de x
On dispose des classes : PaireDEntiers et ListeDePairesDEntiers.
public class PaireDEntiers {
// Paires constantes dentiers <premier,second>
public final int premier; public final int second;
public PaireDEntiers(int p, int s) {premier=p; second=s;}
public boolean equals(PaireDEntiers p2) {
// resultat
: indique si this est egal p
return premier==p2.premier && second==p2.second;
}
}
La classe PaireDEntiers offre des paires constantes, cest--dire une structure avec des
champs non modifiable. Une telle structure reprsente donc une valeur du domaine (entier entier),
inaltrable donc. Cela est dune part naturel et dautre part permet sans aucun soucis de partager
des mmes objets PaireDEntiers dans la reprsentation de plusieurs polynmes. Par exemple
pour la fonction somme , on na besoin de crer une nouvelle PaireDEntiers que lorsque lon
ajoute des monmes non nuls de mme degr. On essayera de profiter de cette opportunit pour
conomiser la mmoire.
La classe ListeDePairesDEntiers a les mmes fonctionnalits que ListeDEntiers . La
seule diffrence est que les lments sont des PaireDEntiers .
Remarque :
on a volontairement pas prvu de constructeur public. Ceci signifie que le constructeur Polynome() est priv. Il est uniquement usage interne, pour construire les rsultats des oprations de
calcul. Cest logique car la classe Polynome reprsente un domaine de valeurs. Tout polynme
sobtient au moyen soit de la constante Polynome.zero , soit du convertisseur partir dun
tableau Polynome.apartirDe(T) , soit par les oprateurs somme(x,y) , produit(x,y) et
derive(x) . Le fait de noffrir aucun constructeur public a pour consquence que lutilisateur de
la classe Polynome naura jamais possibilit de faire new Polynome(), et cest parfaitement
cohrent avec la notion de valeur. En dautres termes, un objet de type Polynome se comporte
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

227

Mise en uvre des ensembles

comme une valeur, par exemple comme lentier bien connu qui sappelle 12, et jamais personne na
eu faire new 12 .
La fonction Polynome.apartirDe(T)

est trs pratique pour programmer les tests.

Tests :
Il vaut mieux faire des tests entirement contenus dans un programme de test plutt que des tests
par lecture des donnes, pour des raisons vidente de reproductibilit et de gain de temps. Les tests
seront programms dans la procdure principale dune classe TestPolynome . On sefforcera
dinventer les tests les plus judicieux possibles. Un bon test nest pas un test qui sarrange pour que
a marche mais qui, au contraire, fait tout pour que a ne marche pas. Il faut certes tester les cas
habituels mais aussi tester les cas particuliers.

Exercice 13.3

Reprsentation dimages noir et blanc

On se propose de reprsenter des images constitues de points noirs ou blancs. Une telle image se
prsente comme un damier de points de taille hauteur largeur.
Chaque point est repr par sa position verticale h et horizontale l.
Le coin suprieur gauche a pour coordonnes (0,0).

largeur

hauteur

On dcide de reprsenter de telles images par une classe ImageBinaire dont les spcifications
sont donnes ci-aprs.

228

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : ensembles

class ImageBinaire { // reprsentations dimages en noir et blanc


public static ImageBinaire blanche(int hauteur, int largeur)
// prrequis
: hauteur>0, largeur>0
// rsultat
: nouvelle image blanche de taille largeur x hauteur
public static
ImageBinaire aPartirDe(int hauteur, int largeur, String s)
// prrequis
: hauteur>0, largeur>0, s.leghth>=hauteur*largeur
// rsultat
: image de taille largeur x hauteur figure par s,
// * pour noir,
. pour blanc, prsente ligne par ligne
public int hauteur() // rsultat

: hauteur de this

public int largeur() // rsultat

: largeur de this

public void noircir(int h, int l)


// prrequis
: 0=<h<hauteur, 0=<l<largeur
// effet
: met noir le point (h,l)
public void blanchir(int h, int l)
// prrequis
: 0=<h<hauteur, 0=<l<largeur
// effet
: met blanc le point (h,l)
public void boolean estNoir(int h, int l)
// prrequis
: 0=<h<hauteur-1, 0=<l<largeur-1
// rsultat
: indique si le point (h,l) est noir
public String toString()
// rsultat
: visualisation textuelle de this
// les points noirs sont des
* et les points blancs des
public ImageBinaire extraitPartieConnexe()
// prrequis
: this nest pas blanche
// rsultat
: une partie connexe (arbitraire) de this (par exemple
// la partie connexe au premier point noir en haut gauche)
// effet : met blanc dans this les points
de cette partie
}

Pour la mise en uvre, on dcide de reprsenter une image au moyen dune matrice M de boolens
de taille hauteur largeur.
M[i][j] = true si le point (i,j) est noir,
M[i][j] = false si le point (i,j) est blanc.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

229

conn

Mise en uvre des ensembles

On commencera par rdiger et tester :


la dclaration des donnes de la classe ImageBinaire ,
le constructeur (priv) qui cre la matrice M, non initialise.
les mthodes hauteur et largeur ,
la fonction statique blanche ,
les mthodes noircir et blanchir ,
la mthode estNoir ,
la fonction apartirDe qui rend en rsultat limage binaire figure par la chane de caractres s.
La chane s prsente limage ligne par ligne et utilise des caractres * pour les points noirs, des
caractres . pour les points blancs. Les caractres autres que * ou . sont autoriss mais
devront tre ignors.
Par exemple, avec hauteur = 10 et largeur = 36, la chane de caractres de gauche donne pour
rsultat limage de droite :
.**.................................
..***.............******............
...****............*******..........
....*****....*******************....
....**************************.****
....*************************...****
...****....************************.
..***........*******************....
.**..............*********..........
....................................

la mthode toString qui rend en rsultat la chane de caractres qui visualise limage, en utilisant des caractres * pour les points noirs, des caractres . pour les points blancs et des passages la ligne \n pour terminer les lignes de limage. Par exemple, pour limage de gauche, le
rsultat est la chane de caractres de droite :
.**.................................
..***.............******............
...****............*******..........
....*****....*******************....
....**************************.****
....*************************...****
...****....************************.
..***........*******************....
.**..............*********..........
....................................

Extraction de parties connexes


Deux points sont adjacents sil se touchent, cest--dire si chacune de leurs coordonnes ne diffrent que dau plus une unit. On appelle partie connexe dun point p dans une image binaire une
image constitue de tous les points noirs que lon peut atteindre depuis p en cheminant par des
points noirs adjacents. Par exemple, limage ci-aprs comporte 3 parties connexes (bonhomme,
petite lune et poisson). On se propose de rdiger la mthode extraitPartieConnexe qui
extrait une partie connexe dune image.

230

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : ensembles

Par exemple, avec limage im suivante :

le rsultat est limage :

et im est change en :

Principe :
On utilise une liste de paires dentiers aVisiter qui contient tout moment de lalgorithme un
ensemble de points qui restent visiter. Ces points visiter sont les points adjacents aux points
noirs dj rpertoris de la partie connexe en cours de construction. On initialise la liste aVisiter
avec un point noir de limage, par exemple le premier point noir rencontr en parcourant limage
ligne par ligne, de gauche droite et on met blanc ce point de limage. Ensuite, tant que la liste
aVisiter nest pas devenue vide :
on retire un point pt de la liste aVisiter ,
on noircit ce point pt dans limage rsultat,
on ajoute aVisiter les points noirs de limage adjacents pt et on les mets blanc dans
limage.
Lorsque la liste aVisiter est devenue vide, limage rsultat est totalement construite : elle contient tous les points noirs de limage origine connexes au premier point noir choisi.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

231

Mise en uvre des ensembles

Exercice 13.4

logique dintervalles

1 - Oprations sur des intervalles dentiers


On dispose de la classe PaireDEntiers :
class PaireDEntiers {
public int premier;
public int second;
public PaireDEntiers(int p, int s) {
premier=p; second=s;
}
}
Dans ce qui suit on reprsente un intervalle dentiers [inf,sup] par une paire dentiers avec premier=inf et second =sup. Une paire dentiers p reprsente un intervalle si p.premier<=p.second .
On dit que deux intervalles dentiers sont dintersection vide si leur intersection est un ensemble
vide. Exemples :
[2,6] et [7,9] sont dintersection vide,
[2,6] et [8,12] sont dintersection vide.
[2,6] et [3,9] ne sont pas dintersection vide,
[2,6] et [3,5] ne sont pas dintersection vide,
tant donns deux intervalles dintersection non vide x et y, intersection(x,y) est lintervalle intersection de x et y. Exemples :
intersection([2,6], [3,9]) = [3,6]

intersection([2,6], [3,5]) = [3,5]

On dispose des fonctions min et max qui rendent en rsultat respectivement le minimum et le
maximum de deux entiers passs en paramtres. On utilisera ces fonctions pour exprimer le plus
simplement possible les rponses aux questions suivantes.
Rdiger la fonction intersectionVide spcifie par :
static boolean intersectionVide(PaireDEntiers x, PaireDEntiers y)
// prrequis
: x et y reprsentent des intervalles dentiers
// rsultat
: indique si x et y sont dintersection vide
Rdiger la fonction IntervalleIntersection spcifie par :
static PaireDEntiers IntervalleIntersection
(PaireDEntiers x,
PaireDEntiers y)
// prrequis
: x et y reprsentent des intervalles
// dintersection non vide
// rsultat
: lintersection de x et y
232

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : ensembles

On dit que deux intervalles dentiers sont fusionnables sils ont une partie commune ou bien sils se
touchent. Exemples :
[2,6] et [3,9] sont fusionnables,
[2,6] et [3,5] sont fusionnables,
[2,6] et [7,9] sont fusionnables.
[2,6] et [8,12] ne sont pas fusionnables.
tant donns deux intervalles fusionnables x et y, on note fusion(x,y) lintervalle union des ensembles reprsents par x et y. Exemples :
fusion([2,6], [3,9]) = [2,9]
fusion([2,6], [3,5]) = [2,6]
fusion([2,6], [7,9]) = [2,9]
Rdiger la fonction fusionnable spcifie par :
static boolean fusionnable(PaireDEntiers x, PaireDEntiers y)
// prrequis
: x et y reprsentent
// des intervalles dentiers
// rsultat
:
// indique si x et y sont fusionnables
Rdiger la fonction fusion spcifie par :
static PaireDEntiers fusion(PaireDEntiers x, PaireDEntiers y)
// prrequis
: x et y sont fusionnables
// rsultat
: la fusion de x et y

2 - Reprsentation des ensembles dentiers par des collections dintervalles


On se propose de reprsenter un ensemble dentiers au moyen dune suite dintervalles non fusionnables, trie par ordre croissant de leurs bornes. Par exemple, les ensembles x et y illustrs par les
traits pais des dessins suivants :

x
y

5
4

seront reprsents par les suites dintervalles

10
8

20
14

25

17

x : [2,5] [7,10] [20,25] et y : [4,8] [14,17]

On dcide de reprsenter un ensemble par un tableau de paires dentiers tab pour mmoriser ses
intervalles, par ordre croissant de leurs bornes, et une variable entire nbElements qui indique le

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

233

Mise en uvre des ensembles

nombre dintervalles. Exemples :

x
tab
nbElements
3
null

10

tab

nbElements
2

20 25

null
null

14 17

Comme le montre ces exemples, le tableau tab nest pas ncessairement entirement utilis.
On dsire programmer une classe EnsembleDEntiers selon ce principe, dote des oprations
usuelles dunion et dintersection et de test dappartenance. Les spcifications de cette classe sont
donnes ci-dessous. Nous insistons sur le fait que les intervalles qui constituent un ensemble sont
non fusionnables deux deux et sont tris par ordre croissant dans tab.
class EnsembleDEntiers {
public static EnsembleDEntiers aPartirDe(int[] T)
// prrequis
: le tableau T contient alternativement
// les bornes inf et sup dintervalles non fusionnables,
// par ordre croissant des bornes
// rsultat
: lensemble constitu de ces intervalles
public boolean contient(int k)
// rsultat
: indique si k appartient this
public static EnsembleDEntiers intersection
(EnsembleDEntiers x, EnsembleDEntiers y)
// rsultat: lintersection de x et de y

public static EnsembleDEntiers union


(EnsembleDEntiers x,EnsembleDEntiers y)
// rsultat: lunion de x et de y

Commencer par rdiger et tester :

Les dclarations de donnes de la classe EnsembleDEntiers ,


Son constructeur : private EnsembleDEntiers(int tailleMax)
qui initialise ltat des donnes de faon reprsenter une collection vide dintervalles capable
de contenir tailleMax intervalles,
La mthode aPartirDe qui rend en rsultat un ensemble form par les intervalles indiqus
dans un tableau dentiers.
Exemple : avec T={2,5,7,10,20,25} ,
le rsultat de EnsembleDEntiers.aPartirDe(T) est lensemble constitu des
intervalles : [2,5] [7,10] [20,25]
La mthode contient qui indique lappartenance dun entier k lensemble.

234

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : ensembles

Opration dintersection
Rdiger et tester la fonction intersection qui rend en rsultat lensemble intersection de x et
de y. On utilisera un algorithme analogue celui vu en cours, qui consiste parcourir les collections dintervalles de x et y par ordre croissant, en progressant dans lensemble qui possde lintervalle de borne suprieure minimale (ou dans les deux si les bornes suprieures sont gales).
Exemple :
2

resul

10

20

14

7 8

25

17
4

En (1) on gnre [4,5] dans le rsultat et on progresse dans x sur lintervalle [7,10]. En (2) on gnre
[7,8] dans le rsultat et on progresse dans y. En (3) on ne gnre rien, car [7,10] et [14,17] sont
dintersection vide, et on progresse dans x. En (4) on ne gnre rien, car [14,17] et [20,25] sont
dintersection vide, et on progresse dans y. Aprs (4), cest termin car un des ensembles, y ici, est
entirement visit.
Pour la taille du rsultat, on utilisera la proprit suivante :
si x et y ne sont pas tous les deux vides,
nombreDIntervalles(intersection(x,y)) nombreDIntervalles(x) + nombreDIntervalles(y) - 1
Programmation de lunion
La fonction union , qui rend en rsultat lensemble union de x et de y, est un peu plus difficile
programmer. Le principe gnral est est le suivant : on progresse dans les ensembles x et y par ordre
croissant des bornes infrieures des intervalles ; chaque intervalle de x ou de y est soit fusionn au
dernier intervalle du rsultat sil est fusionnable avec celui-ci, soit ajout sil nest pas fusionnable.
Exemple :

x
y
resul

5
4

10
14

20

10

14

25

17
5

17 20

25

En (1) on gnre initialement [2,5] dans le rsultat et on progresse dans x. En (2) on fusionne [4,8]
[2,5] ce qui donne [2,8] et on progresse dans y. En (3) on fusionne [7,10] [2,8] ce qui donne
[2,10] et on progresse dans x. En (4) on gnre [14,17] dans le rsultat, car [14,17] et [2,10] ne sont
pas fusionnables, et on progresse dans y. En (5), on gnre [20,25] dans le rsultat, car [14,17] et
[20,25] ne sont pas fusionnables et cest termin.
En analysant cet exemple, on constate quil est souhaitable de se doter dune mthode :
private void cumuleAuBout(PaireDEntiers nouvelIntervalle)
qui, si nouvelIntervalle est fusionnable avec le dernier intervalle de this remplace ce dernier intervalle par sa fusion avec nouvelIntervalle , et sinon ajoute nouvelIntervalle
la suite dans le tableau.
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

235

Mise en uvre des ensembles

Rdiger la mthode cumuleAuBout et lutiliser pou rdiger la fonction union .


Pour la taille du rsultat, on utilisera la proprit suivante :
nombreDIntervalles(union(x,y)) nombreDIntervalles(x) + nombreDIntervalles(y)
Complmentaire
Envisager la programmation dune fonction complementaire qui rend en rsultat le complmentaire dun ensemble.
Il faut pour cela prciser le problme et trouver lalgorithme qui permet dlaborer la reprsentation
du complmentaire :
2

x
?

5
6

10

20

11

19

25
26

complmentaire(x)

Exercice 13.5

Entiers non born

On dsire manipuler les entiers naturels (positifs ou nuls) sans limitation de grandeur (si ce nest par
la capacit mmoire de lordinateur). Ces entiers ne sont bien sr pas reprsentables directement
dans des mots du calculateur qui sont de taille fixe (et petite, 32 ou 64 bits). Nous devons donc les
reprsenter par une classe GrandEntier dont voici les spcifications :
public class GrandEntier {
public static GrandEntier deValeur(int k)
// prrequis
: k>=0
// rsultat
: le GrandEntier de valeur k
public String toString()
// rsultat
: lcriture dcimale de this
public static int comparaison(GrandEntier x, GrandEntier y)
// rsultat
: -1 si x<y, 0 si x=y, +1 si x>y
public static GrandEntier somme(GrandEntier x, GrandEntier y)
// rsultat
: x + y
public static GrandEntier produit(GrandEntier x,GrandEntier y)
// rsultat
:xy
}

Mise en uvre
On dcide de reprsenter un grand entier par la liste de ses chiffres (dcimaux par exemple, bien
quune base plus grande soit plus conomique).
Exemple : lentier 6 227 020 800 sera reprsent par la liste <6,2,2,7,0,2,0,8,0,0>, chaque chiffre
tant un petit entier (compris entre 0 et 9 si la base est dcimale). On normalisera la reprsentation en sinterdisant les chiffres 0 en poids fort.

236

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : arbres

Structures de donnes : arbres

CHAPITRE 14

Un arbre est une structure de donnes qui permet de conserver de linformation de manire hirarchique. Les arbres sont utiliss pour reprsenter des choses aussi diffrentes que :

Une expression arithmtique : (12-3*25)*8 + 4*5 est vue comme un nud + avec comme fils

les deux expressions, lune 12-3*25 et 4*5. A son tour, lexpression 12+3*25 est vue comme un
nud - avec comme fils les deux expressions, 12 et 3*25. Lexpression simple 12 est vue
comme une feuille de larbre (elle est atomique). Lexpression 3*5 est vue comme un nud *
avec comme fils les deux expressions, 3 et 25, toutes deux vues comme des feuilles...

+
*

12

25

Un texte documentaire, tel un cours ou un mode demploi : le document est dcoup en chapi-

tres, eux-mmes dcoupes en sous-chapitres, dcoupes en sous-sous chapitres..., la hirarchie


se terminant par des paragraphes. Les divers niveaux de chapitrage sont vus comme des nuds,
chacun portant comme information son titre et comme fils les niveaux de chapitrage infrieurs
ou les paragraphes quil contient. Les paragraphes sont vues comme les feuilles dun tel arbre.

Lordinateur
1 - Introduction
blabla1 blabla2
2 - Le matriel
2.1 - Lunit centrale
blabla3 blabla4 blabla5
2.2 - La mmoire
blabla6
2.3 - Le disque
blabla7
3 - Les logiciels
3.1 - Le systme
blabla8
3.2 - Les langages
3.2.1 - Java
blabla 9
3.2.2 - C++
blabla10
3.3.3 - Assembleur
blabla11

Lordinateur
Introduction

Les logiciels

Le matriel
Le systme

blabla1

blabla8

Lunit centrale

La mmoire
blabla6

blabla3

Les langages

blabla4

Le disque
blabla7

Java
blabla9

C++

Assembleur

blabla10
blabla11

blabla5

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

237

Spcification de la classe ArbreBinaire

14.1

Spcification de la classeArbreBinaire
Dans un premier temps nous allons nous intresser un cas simple darbres, les arbres binaires. Ce
sont des arbres dont les nuds ont exactement 2 fils, appels gauche et droite. Nous considrons ici
des arbres non modifiables. Un tel arbre est simplement une valeur structure que lon peut :

fabriquer laide de loprateur feuille( i) pour obtenir une feuille porteuse de linformation i,

fabriquer laide de loprateur noeud( i,g,d) pour obtenir un nud porteur de linformation

i et de fils g et d,
ou que lon peut obtenir en tant que sous-arbres gauche ou droite dun nud par les oprations
gauche et droite .
Voici la spcification propose pour la classe ArbreBinaire :
class ArbreBinaire {
public static ArbreBinaire
noeud(String info,ArbreBinaire g,ArbreBinaire d)
// rsultat
: le noeud <info,g,d>
public static ArbreBinaire feuille(String info)
// rsultat
: la feuille <info>
public static ArbreBinaire aPartirDe(String s)
// rsultat
: larbre binaire figur par s selon une notation
// parenthse prfixe
public boolean estNoeud()
// rsultat
: indique si this est un noeud
public boolean estFeuille()
// rsultat
: indique si this est une feuille
public String info()
// rsultat
: linfo associe this
public ArbreBinaire gauche()
// prrequis
: this est un noeud
// rsultat
: le fils gauche de this
public ArbreBinaire droite()
// prrequis
: this est un noeud
// rsultat
: le fils droite de this
public String toString()
// rsultat
: this en clair sous forme parenthse prfixe

238

public String enClairInfixe()


// rsultat
: this en clair sous forme parenthse infixe

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : arbres

14.2

Exemples simples dutilisation de la classe ArbreBinaire


Avant dtudier une mise en uvre possible, voici quelques exemples simples qui illustrent les utilisations possibles de la classe ArbreBinaire . On considre la reprsentation dexpressions arithmtiques. Dans cet exemple :

les informations associes aux nud sont des symboles dopration "+" et "*",
les informations associes aux feuilles sont des valeurs numriques exprimes en dcimal, par
exemple "3622" .

Soit lexpression expr1= 12*56 +24,


elle scrit de faon infixe : +(*(12,56),24).
On peut obtenir larbre correspondant par :
ArbreBinaire expr1 = ArbreBinaire.apartirDe("+(*(12,56),24)");
On pourrait galement obtenir cet arbre par la squence :
ArbreBinaire
ArbreBinaire
ArbreBinaire
ArbreBinaire
ArbreBinaire

d12 = ArbreBinaire.feuille(12);
d56 = ArbreBinaire.feuille(56);
mulD12D56 = ArbreBinaire.noeud("*",d12,d56);
d24 = ArbreBinaire.feuille(24);
expr1=ArbreBinaire.noeud("+",mulD12D56,d24);

ou encore, sans identifications intermdiaires :


ArbreBinaire expr1=
ArbreBinaire.noeud(
"+",
ArbreBinaire.noeud(
"*",
ArbreBinaire.feuille(12),
ArbreBinaire.feuille(56)
),
ArbreBinaire.feuille(24)
);
On obtient ainsi larbre que lon peut figurer par le dessin suivant :

*
12

56

24

On peut capter le sous-arbre de gauche de expr1 par :


ArbreBinaire expr2 = expr1.gauche();
expr2 est larbre suivant (baptis mulD12D56 prcdemment) :

*
12

56

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

239

Mise en uvre de la classe ArbreBinaire

14.3

Mise en uvre de la classe ArbreBinaire


Nous allons reprsenter un arbre binaire au moyen des attributs de donnes suivant :

sorte : lindication de sa sorte, nud ou feuille. Le type de cet attribut sera dun type numr
Sorte ,

info : linformation associe, de type String ,


et, utiles uniquement dans le cas dun nud :

gauche : la rfrence au fils gauche, de type ArbreBinaire ,


droite : la rfrence au fils droite, de type ArbreBinaire .
Exemples :
reprsentation de la feuille 12
sorte
info
gauche
droite

reprsentation du nud *(12,56)


sorte
info
gauche
droite

feuille
"12"

?
?

sorte
info
gauche
droite

noeud
"*"

feuille
"12"

?
?

sorte
info
gauche
droite

feuille
"56"

?
?

Ceci conduit aux dclarations dattributs de donnes et aux constructeurs suivants :


class ArbreBinaire {
private static enum Sorte {noeud, feuille};
private Sorte sorte;
private String info;
// cas dun noeud
private ArbreBinaire gauche;
private ArbreBinaire droite;
private ArbreBinaire(String info){
// constructeur
: feuille <info>
sorte = Sorte.feuille;
this.info=info;
}
private ArbreBinaire(String info,
ArbreBinaire g,ArbreBinaire d){
// constructeur
: noeud <info,gauche,droite>
sorte = Sorte.noeud;
this.info=info; gauche=g; droite=d;
}

240

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : arbres

Les constructeurs sont privs : lutilisateur ne pourra faire new ArbreBinaire(...). Cest
une bonne discipline lorsque, comme cest la cas ici, on reprsente un domaine de valeurs et non
pas de vrais objets. On ne cre pas proprement parler un arbre, mais on le dnote au moyen
doprations telles que noeud , feuille ou aPartirDe . Cest comme pour les entiers, on ne
cre pas 12, on le dnote par 12 ou par 8+4 ou par 2*6...
Les opration les plus simples qui fabriquent des arbres sont feuille et noeud :
public static ArbreBinaire feuille(String info) {
// rsultat
: la feuille <info>
return new ArbreBinaire(info);
}
public static ArbreBinaire
noeud(String info,ArbreBinaire g,ArbreBinaire d){
// rsultat
: le noeud <info,g,d>
return new ArbreBinaire(info,g,d);
}

Lopration suivante aPartirDe permet dobtenir larbre dnot par une chane de caractres
sous forme parenthse prfixe :

la feuille <info> est simplement dnote par la chane de caractre info,


le nud < info,g,d> est dnote par la chane de caractre info(gg,dd) o gg et dd sont les
chanes de caractres qui dnotent respectivement g et d.

Cette fonction nest pas simple raliser. En toute rigueur, sa ralisation exige une analyse syntaxique de la chane de caractres qui dnote larbre, mais cela nous emmnerait trop loin. Ici, on
peut profiter de ce que le prrequis nous garantit que la chane est correcte (sans erreur de
syntaxe) : on peut alors sen tirer au moyen de deux fonctions (prives) auxiliaires :

extraitInfo(String

quis)
rend en rsultat la sous-chane du dbut de s qui constitue la
donne info de larbre : cette sous-chane commence au dbut de s et sarrte soit la rencontre
du premier caractre sparateur '(' ou ',' ou bien la fin de s.
Exemples :
extraitInfo("add(mul(12,56),24)") vaut "add"
extraitInfo("12") vaut "12"

extraitMembre(String

s, int
qui rend
i) en rsultat la sous-chane de s qui commence en position i et reprsente un sous-arbre gauche ou droite dun noeud : cette souschane se termine sur le premier caractre sparateur ',' ou ')' rencontr qui ne soit pas
inhib par un prenthsage (...).
Exemples :
extraitMembre("add(mul(12,56),24)",4) vaut "mul(12,56)"
extraitMembre("add(mul(12,56),24)",15) vaut "24"

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

241

Mise en uvre de la classe ArbreBinaire

private static String extraitInfo(String s){


// rsultat
: sous-chane de s commenant en 0 et se terminant
// au premier sparateur
( ou
, (exclu) rencontr
// ou la fin de s
int i=0;
while(i<s.length() && s.charAt(i)!=( && s.charAt(i)!=,){
i++;
}
if(i==s.length()){return s;}
else {return s.substring(0,i);}
}
private static String extraitMembre(String s,int i){
// rsultat
: sous-chaine de s, partir de i, se terminant au
// premier sparateur
) ou
, non inhib par un parenthsage
// (...)
int j=i;
int niveau=0;
while(niveau!=0 || (s.charAt(j)!=) && s.charAt(j)!=,)){
if(s.charAt(j)==(){niveau++;}
else if(s.charAt(j)==)){niveau--;}
j++;
}
return s.substring(i,j);
}
Dot de ces deux fonctions, nous sommes en mesure de programmer aPartirDe . Sa ralisation
est naturellement rcursive :
public static ArbreBinaire aPartirDe(String s){
// rsultat
: larbre binaire figur par s selon une notation
// parenthse prfixe
String info=extraitInfo(s);
if(info.length()==s.length()){ // feuille
return feuille(info);
}
else{ // noeud
String sg=extraitMembre(s,info.length()+1);
String sd=
extraitMembre(s,info.length()+1+sg.length()+1);
return noeud(info,aPartirDe(sg),aPartirDe(sd));
}
}

On extrait la partie info en dbut de s. Si cette partie info est s dans son intgralit, s reprsente la
feuille feuille( info). Sinon s reprsente un nud : on extrait les chanes sg et sd qui reprsentent les fils gauche et droite et s reprsente le nud noeud( gauche,droite).

242

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : arbres

Les fonctions estNoeud , estFeuille , gauche et droite sont particulirement simples


raliser car leur rsultat sobtient directement par un attribut de lobjet :
public boolean estNoeud(){
// rsultat
: indique si this est un noeud
return sorte==Sorte.noeud;
}
public boolean estFeuille(){
// rsultat
: indique si this est une feuille
return sorte==Sorte.feuille;
}
public String info(){
// rsultat
: linfo associe this
return info;
}
public ArbreBinaire gauche(){
// prrequis
: this est un noeud
// rsultat
: le fils gauche de this
return gauche;
}
public ArbreBinaire droite(){
// prrequis
: this est un noeud
// rsultat
: le fils droite de this
return droite;
}
Les fonctions de visualisation en clair, toString et enClairInfixe , sont simple raliser de
faon rcursive. Pour une feuille, le rsultat est lattribut info, et pour un nud le rsultat
sobtient soit par concatnation de info, de parenthses, de virgule et de visualisation en clair des
fils gauche et droite :
public String toString() {
// rsultat
: this en clair sous forme parenthse prfixe
if (sorte==Sorte.noeud){
return info+"("+gauche+","+droite+")";
}
else /* sorte==feuille */ {return info;}
}

public String enClairInfixe() {


// rsultat
: this en clair sous forme parenthse infixe
if (sorte==Sorte.noeud){
return "("+gauche.enClairInfixe()
+info+droite.enClairInfixe()+")";
}
else /*sorte==feuille*/ {return info;}
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

243

Autre exemple dutilisation de ArbreBinaire

14.4

Autre exemple dutilisation de ArbreBinaire


Les exemples suivants utilisent, comme prcdemment, des arbres binaires pour reprsenter des
expressions arithmtiques. Les oprations sont (par exemple) addition et multiplication. Ces
oprations sont notes par les symboles "+" et "*".
On peut vouloir traiter des expressions arithmtiques dans lesquelles les oprations sont notes par
les chanes de caractres "add" et "mul" . Un besoin se fait sentir de vouloir remplacer ces chanes par les symboles "+" et "*" pour en quelque sorte les normaliser. Cest ce que ralise la
fonction suivante :
static ArbreBinaire
remplaceInfoNoeud(String s1,String s2,ArbreBinaire a){
// rsultat
: arbre obtenu en remplaant dans a
// les infos des noeuds gaux s1 par s2
if(a.estNoeud()){
String i=a.info(); if(i.equals(s1)){i=s2;}
return ArbreBinaire.noeud(i,
remplaceInfoNoeud(s1,s2,a.gauche()),
remplaceInfoNoeud(s1,s2,a.droite())
);
}
else /* a.estFeuille() */ {return a;}
}

Ainsi, lexpression expr1 :


ArbreBinaire expr1 =
ArbreBinaire.aPartirDe("add(12,mul(25,add(mul(34,56),78))))");
Pourra tre convertie en une expression expr2 dont la forme en clair serait
"+(12,*(25,+(mul(34,56),78))))"
grce :
ArbreBinaire expr2 =
remplaceInfoNoeud("add","+",remplaceInfoNoeud("mul","*",expr1));

Comme le montre cet exemple, les parcours darbres sont naturellement rcursifs : pour remplacer
s1 par s2 dans un arbre qui est un nud, on utilise le remplacement de s1 par s2 dans les fils gauche
et droite et on recre le nud en remplaant ventuellement linformation associe.

244

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : arbres

La fonction suivante value une expression arithmtique constitues doprations notes "+" et
"*", cest--dire rend le rsultat numrique du calcul suggr par lexpression :
static int eval(ArbreBinaire expr) {
// prrequis
: expr reprsente un expression entire
// avec "+" ou "*" pour info associes aux noeuds et des
// reprsentation dcimales de nombres associes aux feuilles
// rsultat
: lvaluation de expr
if(expr.estFeuille()) {return Integer.valueOf(expr.info());}
else /* expr.estNoeud() */ {
if (expr.info().equals("+")){
return eval(expr.gauche())+eval(expr.droite());
}
else /* expr.info().equals("*") */ {
return eval(expr.gauche())*eval(expr.droite());
}
}
}
On peut ainsi construire une expression et lvaluer :
ArbreBinaire expr=ArbreBinaire.aPartirDe("+(*(12,56),24)");
System.out.println("expr = "+expr);
System.out.println(
"expr.enClairInfixe() = "+expr.enClairInfixe());
System.out.println("eval(expr) = "+eval(expr));
Ce morceau de programme affiche :
expr = +(*(12,56),24)
expr.enClairInfixe() = ((12*56)+24)
eval(expr) = 696

14.5

Spcification de la classeArbre
Nous allons nous intresser maintenant aux arbres dont chaque nud peut avoir un nombre quelconque de fils. Ces arbres seront offerts par la classe Arbre . La spcification de la classe Arbre
ressemble beaucoup celle de ArbreBinaire . Les seules diffrences proviennent de la multiplicit quelconque des fils dun nud. Ces diffrences sont :

la cration dun nud , ralise par la fonction :


Arbre noeud(String info, Arbre[] fils)
qui a pour paramtre un tableau darbres pour indiquer les fils du nud,
laccs aux fils dun nud qui ne peut plus se faire au moyen de gauche et droite puis quil
y a un nombre quelconque de fils; cet accs se fait au moyen de la mthode :
public Parcours<Arbre> parcoursLesFils()

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

245

Exemple dutilisation de la classe Arbre

qui rend en rsultat un parcours initialis sur le premier fils du nud; les fils pourront alors tre
obtenus par les mthodes usuelles dun parcours : elementCourant , suivant et le test
estEnFin .
class Arbre {
public static Arbre noeud(String info, Arbre[] fils)
// rsultat
: larbre noeud<info,fils[0]...>
public static Arbre feuille(String info)
// rsultat
: larbre feuille<info>
public boolean estNoeud()
// rsultat
: indique si this est un noeud
public boolean estFeuille()
// rsultat
: indique si this est une feuille
public String info()
// rsultat
: linfo associe this
public Parcours<Arbre> parcoursLesFils()
// prrequis
: this est un Noeud
// rsultat
: parcours initialis sur le premier fils de this
public static Arbre aPartirDe(String s)
// rsultat
: larbre binaire figur par s selon une notation
// parenthse prfixe

14.6

public String toString()


// rsultat
: this en clair sous forme parenthse prfixe

Exemple dutilisation de la classe Arbre


Avant dtudier une mise en uvre possible, voici un exemple simple qui illustre une utilisation de
la classe Arbre .
On considre la reprsentation de documents. Dans cet exemple :

les informations associes aux nud sont des titres de chapitres de divers niveaux,
les informations associes aux feuilles sont les textes des paragraphes du document.
Voici un exemple dun tel document :

246

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : arbres

Le monde animal
1 - Les mollusques
Ils sont mous
1.1 - les bivalves
ils ont deux valves
1.2 - Les gastropodes
Ils ont une coquille en colimaon
Ils ont deux cornes
2 - Les vertbrs
2.1 - Les poissons
Ils nagent
2.2 - les reptiles
Ils marchent ou rampent
Ils ont le sang froid
2.3 - Les mammifres
Ils ont le sang chaud
2.3.1 - Les ctacs
Ils nagent
2.3.2 - Les bovids
Ils marchent
Ils vivent dans les pturages
3 - Conclusion
voila cest fini

Pour obtenir larbre correspondant ce document, il suffit dutiliser la fonction aPartirDe :


Arbre document = Arbre.aPartirDe(
"Le monde animal("
+ "1 - Les mollusques("
+ "Ils sont mous,"
+ "1.1 - Les bivalves(ils ont deux valves),"
+ "1.2 - Les gastropodes("
+ "Ils ont une coquille
en colimaon,Ils
ont
+ "),"
+ "2 - Les vertbrs("
+ "2.1 - Les poissons(Ils nagent),"
+ "2.2 - Les reptiles("
+ "Ils marchent ou rampent,Ils ont le sang froid)"
+ "2.3 - Les mammifres(Ils ont le sang chaud),"
+ "2.3.1 - Les ctacs(Ils nagent),"
+ "2.3.2 - Les bovids("
+ "Ils marchent,Ils vivent dans les pturages)"
+ "),"
+ "3 - Conculsion(voila cest fini)"
+")"
);

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

deux

247

cornes)"

Exemple dutilisation de la classe Arbre

Si on visualise cet arbre par la mthode toString() :


System.out.println(document);
on obtient :

Le monde
animal(1
- Les mollusques(Ils
sont mous,1.1
- Les bivalves(Ils
ont
deux
valves),1.2
- Les
gastropodes(Ils
ont
une
coquille
en colimaon,Ils
ont deux cornes)),2
- Les vertbrs(2.1
Les poissons(ils
nagent),2.2
- Les reptiles(Ils
marchent
ou rampent,Ils
ont le sang froid),2.3
- Les mammifres(ils
ont le sang
chaud,2.3.1
- Les ctacs(Ils
nagent),2.3.2
- Les bovids(Ils
ma
chent,Ils
vivent
dans les pturages))),3
- Conculsion(voila
ces
fini))
Ce nest pas trs joli... mais linformation y est.
Un traitement que lon peut vouloir faire sur un tel document est de lindenter. Ceci consiste
rajouter un certain nombre despaces supplmentaires gauche chaque passage un niveau de
chapitrage infrieur. Cette fonction dindentation pourra tre ralise par la fonction suivante :
static String indente(Arbre a) {
// rsultat
: a en clair indent
return indente(a,0);
}
static String indente(Arbre a,int i) {
// rsultat
: a en clair indent de i espaces
if (a.estNoeud()){
String resul=blancs(i)+a.info()+"\n";
Parcours<Arbre> p = a.parcoursLesFils();
while(!p.estEnFin()){
resul=resul+indente(p.elementCourant(),i+4);
p.suivant();
}
return resul;
}
else /* a.estFeuille() */{
return blancs(i)+a.info()+"\n";
}
}
static String blancs(int i){
// rsultat
: une chane de i espaces
String resul="";
for(int k=0; k<i; k++){resul=resul+" ";}
return resul;
}
Cest une fonction naturellement rcursive. Plus exactement la fonction proprement rcursive est
static String indente(Arbre a,int i)
qui a un paramtre de plus, i, qui est la quantit initiale dsire despaces gauche.
La fonction static String indente(Arbre a) appelle cette dernire avec i=0.

248

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : arbres

Si on imprime le rsultat de lindentation :


System.out.println(indente(document));
On obtient le texte indent :
Le monde animal
1 - Les mollusques
Ils sont mous
1.1 - Les bivalves
ils ont deux valves
1.2 - Les gastropodes
Ils ont une coquille en colimaon
Ils ont deux cornes
2 - Les vertbrs
2.1 - Les poissons
Ils nagent
2.2 - Les reptiles
Ils marchent ou rampent
Ils ont le sang froid
2.3 - Les mammifres
2.3.1 - Les ctacs
Ils nagent
2.3.2 - Les bovids
Ils marchent
Ils vivent dans les pturages
3 - Conclusion
voila cest fini

14.7

Mise en uvre de la classe Arbre


Nous allons reprsenter un arbre au moyen des attributs de donnes suivant :

sorte : lindication de sa sorte, nud ou feuille.


info : linformation associe, de type String ,
et, utiles uniquement dans le cas dun nud :

lesFils : une liste dlments de type Arbre .


Voici les dclarations des attributs de donne et les constructeurs :
class Arbre {
public static enum Sorte
{noeud,feuille}; private
// cas dun noeud
Liste<Arbre> lesFils;
// cas dun noeud ou dune feuille
private String info;

Sorte sorte;

private Arbre(String info){


// constructeur
: arbre feuille<info>
sorte = Sorte.feuille; this.info=info;
}
private Arbre(String info, Liste<Arbre> fils){
// constructeur
: arbre noeud<info,liste des fils>
sorte = Sorte.noeud; this.info=info; lesFils=fils;
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

249

Mise en uvre de la classe Arbre

Les fonctions (publiques) de fabrication des nuds et des feuilles :


public static Arbre noeud(String info, Arbre[] fils) {
// rsultat
: larbre noeud<info,fils[0]...>
Liste<Arbre> lesFils=new Liste<Arbre>();
for(int i=0;i<fils.length;i++){
lesFils.ajouteEnQueue(fils[i]);
}
return new Arbre(info,lesFils);
}
public static Arbre feuille(String info) {
// rsultat
: larbre feuille<info>
return new Arbre(info);
}

Les fonctions estNoeud , estFeuille et info sont strictement identiques celles de


ArbreBinaire :
public boolean estNoeud(){
// rsultat
: indique si this est un noeud
return sorte==Sorte.noeud;
}
public boolean estFeuille(){
// rsultat
: indique si this est une feuille
return sorte==Sorte.feuille;
}
public String info(){
// rsultat
: linfo associe this
return info;
}

La fonction parcoursLesFils rend simplement un parcours sur lesFils :


public Parcours<Arbre> parcoursLesFils(){
// prrequis
: this est un Noeud
// rsultat
: parcours initialis sur le premier fils de this
return lesFils.nouveauParcours();
}

La ralisation de aPartirDe est un peu plus complexe. La mthode utilise est similaire celle
de ArbreBinaire , mais au lieu dextraire les chanes qui reprsentent les fils gauche et
droite dun nud, il faut extraire une quantit non dtermine lavance de chanes qui reprsentent les fils du nud.

250

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Structures de donnes : arbres

private static String extraitInfo(String s){


// rsultat
: sous-chane de s commenant en 0 et se terminant
// au premier caractre sparateur
( ou
, (exclu) rencontr
// ou la fin de s
int i=0;
while(i<s.length()
&& s.charAt(i)!=(
&& s.charAt(i)!=,){
i++;
}
if(i==s.length()){return s;}
else {return s.substring(0,i);}
}
private static String extraitMembre(String s,int i){
// rsultat
: sous-chaine de s, partir de i, se terminant au
// premier sparateur
) ou
, non inhib par un parenthsage
// ouverture
( fermeture
)
int j=i;
int niveau=0;
while(niveau!=0 || (s.charAt(j)!=) && s.charAt(j)!=,)){
if(s.charAt(j)==(){niveau++;}
else if(s.charAt(j)==)){niveau--;}
j++;
}
return s.substring(i,j);
}
public static Arbre aPartirDe(String s){
// rsultat
: larbre binaire figur par s selon une notation
// parenthse prfixe
String info=extraitInfo(s);
if(info.length()==s.length()){ // feuille
return feuille(info);
}
else{ // noeud
int j=info.length()+1;
Liste<Arbre> lesFils = new Liste<Arbre>();
if(s.charAt(j)!=)){ // on autorise un noeud avec 0 fils
String sFils=extraitMembre(s,j);
lesFils.ajouteEnQueue(aPartirDe(sFils));
j=j+sFils.length();
while(s.charAt(j)==,){
j=j+1;
sFils=extraitMembre(s,j);
lesFils.ajouteEnQueue(aPartirDe(sFils));
j=j+sFils.length();
}
}
return new Arbre(info,lesFils);
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

251

Mise en uvre de la classe Arbre

Pour terminer, la fonction toString , trs semblable celle de ArbreBinaire :


public String toString() {
// rsultat
: this en clair sous forme parenthse prfixe
if (sorte==Sorte.noeud){
String resul =
info+"(";
Parcours<Arbre> p = lesFils.nouveauParcours();
if(!p.estEnFin()){ /* liste de fils non vide */
resul = resul+p.elementCourant();
p.suivant();
while(!p.estEnFin()){
resul=resul+","+p.elementCourant();
p.suivant();
}
}
return resul+")";
}
else /* sorte==feuille */ {
return info;
}
}

252

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Reprsentation des informations dans les ordinateurs

CHAPITRE 15

15.1
15.1.1

Reprsentation des informations


dans les ordinateurs

Nombres et reprsentations de nombres


Notion de reprsentation
Les nombres entiers sont une abstraction. On peut dfinir les nombres entiers de faon informelle :
un nombre entier est la mesure de la quantit de choses individuellement discernables, par exemple
une quantit de pommes ou de billes. On peut les dfinir plus formellement : cest un ensemble,
habituellement appel N, qui satisfait aux axiomes de larithmtique.
Au sujet des nombres, nous sommes amens faire des calculs. Un calcul, quil soit fait la main
ou par une machine, est ncessairement un processus physique : il doit travailler sur des marques
concrtes qui reprsentent les nombres, selon certaines conventions.
Nous connaissons plusieurs systmes de marques concrtes pour reprsenter les nombres, par
exemple le nombre 12 peut tre reprsent par :
un lot de 12 billes

des chiffres romains

XII
une suite de deux chiffres dcimaux

12

une suite de quatre chiffres binaires

1100

Il ne faut pas confondre nombre, entit abstraite, et reprsentation de nombre, marque concrte qui
permet de faire des calculs au moyen de machines (ventuellement humaines) excutant des algorithmes. Si on accepte cette distinction, il ny a pas de nombres dans les machines, et il ny en aura
jamais, il ny a que des reprsentations de nombres. Ce nest pas un problme technologique, cest
par nature quun nombre (ou toute autre entit abstraite) na pas sa place dans une machine.

15.1.2

Numrations positionnelles
Les reprsentations des nombres les plus utilises sont les reprsentations positionnelles. Dans une
telle reprsentation, on utilise une collection finie (et gnralement petite) de symboles appels
chiffres.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

253

Nombres et reprsentations de nombres

On utilise gnralement les symboles suivants :

2 3 4 5 6 7 8 9 A B C D E F ...

chiffres binaires
chiffres dcimaux
chiffres hexadcimaux
Le nombre de chiffres utiliss sappelle la base.
Les bases les plus utilises sont la base 2 (deux chiffres 0 et 1 appels chiffres binaires), la base 10
(dix chiffres de 0 9 appels chiffres dcimaux) et la base 16 (seize chiffres de 0 9 puis de A F
appels chiffres hexadcimaux).
La base 60 est galement utilise, de faon limite mais quotidienne, pour compter le temps en heures/minutes/secondes.
Dans une reprsentation positionnelle en base b, chaque chiffre on associe une valeur de 0 b-1 :

0 1 2 3 4 5 6 7 8 9 A B C D E F

val(c)

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

chiffre
valeur entire
associe

Un nombre est reprsent par une succession de chiffres cn-1cn-2...c1c0. Le nombre reprsent est :

interprtationb(cn-1cn-2...c1c0) = val(cn-1)bn-1+ val(cn-2)bn-2 +... val(c1)b + val(c0)

On a coutume dappeler interprtation la fonction qui va des marques concrtes de la reprsentation (succession de chiffres ici) vers les valeurs abstraites reprsentes (un nombre entier ici). Pour
distinguer les diverses fonctions dinterprtation associes aux diverses bases, nous avons plac la
base en indice du nom de la fonction.
Exemples :
interprtation en base 10 de 3058 :
interprtation10(3058) = 3103+0102+510 +8 = 3058

(on sy attendait)

interprtation en base 2 de 1101 :


interprtation2(1101) = 123+122+02+1 = 8+4+1 = 13
interprtation en base 16 de 1A4 :
interprtation16(1A4) = 1162+1016+4 = 256+160+4 = 420
On a coutume dappeler reprsentation la fonction inverse qui va des valeurs abstraites vers les
marques concrtes de la reprsentation. La mthode pour trouver la reprsentation en base b dun
nombre consiste diviser itrativement par la base. Les chiffres de la reprsentation sont les chiffres
correspondants aux restes des divisions successives.

254

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Reprsentation des informations dans les ordinateurs

Exemples : reprsentations en base 10 et en base 2 :


3058
8

10
305
5

10
30
0

13
1
10
3
3

reprsentation10(3058) = 3058

10
0
(on sy attendait)

2
6
0

2
3
1

2
1
1

2
0

reprsentation2(13) = 1101

Les reprsentations positionnelles sont pratiques pour effectuer les calculs, notamment addition et
soustraction selon les algorithmes bien connus enseigns lcole primaire.
Une invention essentielle de ce mode de reprsentation fut le chiffre 0. Il sert, comme on le sait,
indiquer quil ny a aucun terme bi en position i. Son rle est donc essentiellement doccuper une
place o il ny a rien. Avant linvention du 0, il fallait user de priphrases pour citer certains
nombres : 246 scrivait 246, alors que 206 devait snoncer par quelque chose comme 2 centaines
et 6.

15.1.3

Chiffres binaires : bit


Les machines informatiques utilisent la base 2, car le plus petit lment dinformation que lon sait
raliser conomiquement et avec fiabilit possde 2 valeurs, 0 et 1. Un chiffre binaire est souvent
appel bit, abrviation pour binary digit, terme anglais pour chiffre binaire.
cause de son petit nombre de chiffres, lcriture binaire est peu lisible pour lhomme : il est difficile de voir si deux suites de chiffres binaires sont gales, ou de voir lequel de deux nombres reprsents est le plus grand.
Pour faciliter la lecture, on utilise souvent une base cousine, la base 16. La correspondance entre
base 2 et base 16 est triviale car 16=24. Il suffit donc de regrouper les chiffres binaires par 4 et les
remplacer par leur correspondant en base 16 :
Binaire
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111

Hexadcimal
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F

Exemple :
le nombre qui se reprsente en binaire par
0101 1011 0011 0001
se reprsente en hexadcimal par :
5B31
Remarque : ceci est une proprit gnrale. Le
passage de la reprsentation en base b la reprsentation en base bk se fait en regroupant les chiffres par paquets de k chiffres et en remplaant
chaque paquet par le chiffre correspondant de la
base bk. Ici nous sommes passs de la base b=2
la base bk=24=16.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

255

Mmoire

15.2

Mmoire
La mmoire est le composant dun ordinateur dans lequel le processeur range et rcupre les informations. Le plus petit lment de mmorisation est capable de conserver un bit. Cest un lment
lectronique deux tats stables dans lequel linformation est reprsente par un potentiel lectrique valant 0v ou +5v. Une mmoire de un bit permet donc la reprsentation dune valeur binaire,
selon une convention, par exemple 0 : 0v et 1 : +5v.
Les bits sont regroups en paquets de taille fixe appels mots.
Les tailles les plus courantes sont 8 bits, appel aussi octet (byte en anglais), 16 bits et 32 bits.
0110 0011 0100 1011 0000 1000 0101 0111

8 bits (octet)
16 bits
32 bits
Les mots dune mmoire sont reprs par un numro appel adresse. Depuis plusieurs annes,
loctet sest impos comme unit adressable. Pour lire ou crire des donnes dans la mmoire, le
processeur indique ladresse du mot concern et change la donne au moyen dun bus de donnes.
processeur
manipule les donnes

0
1
2
3
4

adresse
repre les donnes

0110
1110
0110
0000
0100

0011
0001
1011
1011
1111

bus : transmet les donnes


entre mmoire et processeur

mmoire : conserve les donnes

0010 0011
0110 0001

15.3

Reprsentations usuelles des types simples


Nous prsentons succinctement dans ce paragraphe les reprsentations usuelles des types simples
dans les ordinateurs :

caractres,
nombres entiers positifs,
nombres entiers relatifs,
nombres rels.
Dans un ordinateur, tous les types de donnes sont ncessairement reprsents par des suites finies
de bits. Une suite de n bits peut prendre 2n valeurs et peut donc reprsenter un domaine dau plus 2n
lments. part cette contrainte sur sa taille, le domaine reprsent peut tre quelconque : il suffit
dattribuer une signification particulire chacune des suites de bits.
256

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Reprsentation des informations dans les ordinateurs

Exemples :
Avec 2 bits x1x0, on dispose de 22 = 4 valeurs, avec lesquels on peut reprsenter les domaines
suivants :
bits

ensemble de

ensemble de

x1x0

4 couleurs

4 lettres

4 nombres

00

rouge

avancer

01

bleu

reculer

10

vert

tourner

11

jaune

arrter

4 instructions

Comme le montrent ces exemples, une suite de bits (plus gnralement une collection de marques
concrtes) na pas de signification en soi. Cest seulement travers une interprtation particulire
quelle prend un sens. Il est important de remarquer que les interprtations ne sont pas du domaine
des machines. Ce sont toujours des tres humains, ou des phnomnes physiques dans le cas
dapplication de contrle dappareils, qui en dernier ressort interprtent linformation.
Dans les langages de programmation, cest grce en partie la notion de type que le programmeur
indique linterprtation quil attribue ses donnes. Ceci permet au compilateur de choisir la reprsentation convenable en terme de bits, et sert de garde fou en nautorisant sur ces reprsentations
que les oprations qui ont un sens dans le cadre de ces interprtations : addition et soustraction pour
les entiers, test pour les boolens, concatnation pour les chanes de caractres...
Exemple :
Avec les reprsentations sur 2 bits voques prcdemment, le programmeur ayant dclar une
variable coul, de type couleur , il pourra excuter laffectation coul=vert . Le compilateur
choisira une mmoire de deux bits dadresse coul (deux bits choisis dans un octet puisque la
mmoire est adressable par octets).
Laffectation :
sera traduite par le compilateur en :

coul=vert;
ranger dans la mmoire dadresse coul les bits 10

Le programmeur pourra utiliser toute opration ayant un sens pour les couleurs, par exemple les
fonctions estCouleurFroide ou estCouleurChaude rsultat boolen qui indiquent respectivement si une couleur est froide (vert, bleu) ou chaude (rouge, jaune). Il pourra crire :
if (estCouleurFroide(coul)) {...}
cela sera accept et traduit en
si la mmoire dadresse coul contient 01 ou 10 : ...
En revanche, le compilateur refusera linstruction :
coul = coul+1;
car cest un non-sens dincrmenter une couleur, bien que la machine sache incrmenter 10 pour
donner 11.

15.3.1

Reprsentation des caractres : ASCII et UNICODE


Les caractres sont les marques qui servent constituer des textes. Une reprsentation des caractres doit dabord fixer lensemble des caractres que lon reprsente. La reprsentation actuellement
la plus rpandue (en 2001) est la reprsentation ASCII1. Elle reprsente un ensemble de 128 caracModule A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

257

Reprsentations usuelles des types simples

tres sur 7 bits (128=27). La reprsentation est indique sur la table suivante :
b6-4

b3-0

0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111

000 001 010 011 100 101


SP
0
P
@
NUL DLE
!
1
Q
A
SOH DC1
STX DC2
"
2
R
B
#
3
S
C
ETX DC3
4
T
D
$
EOT DC4
%
5
U
E
ENQ NAK
ACK SYN
&
6
V
F
7
W
G
BEL ETB
'
(
X
H
8
CAN
BS
)
Y
I
9
SKIP EM
SUB
Z
J
:
LF
*
+
ESC
VT
[
K
;
,
FS
L
<
FF
GS
=
M
]
CR
N
^
.
>
HOME
SO
_
O
/
?
NL
SI

110 111
`
p
a
q
b
r
c
s
d
t
e
u
f
v
g
w
h
x
i
y
j
z
k
{
l
m
}
n
o
DEL

Les 7 bits sont nots b6b5b4b3b2b1b0. Par exemple, 1000001 reprsente 'A', 1000010 reprsente
'B', 110001 reprsente 'a'... Certains caractres ne sont pas des marques imprimables mais des
caractres de contrle ou de mise en page : CR (Carriage Return) positionnement en dbut de ligne,
LF (Line Feed) passage la ligne suivante, BEL (Bell) sonnerie...
Ce jeu de caractres est cependant limit 127 lments. Il ne permet pas de reprsenter les caractres accentus ni les caractres spcifiques certaines langues.
Lunit de stockage est loctet, et non pas 7 bits. Lorsquune reprsentation ASCII de caractre est
range dans un octet, le bit de poids fort de loctet, le bit 7, inutilis, est mis 0. La reprsentation
ASCII tendue sur 8 bits permet dtendre le rpertoire 256 caractres.
Plus rcemment est apparue une nouvelle reprsentation, sur 32 et 16 bits, appele UNICODE.
Cest un standard destin remplacer peu peu lusage de lASCII. Il est deux fois plus coteux en
place mais il permet de reprsenter 216 caractres, soit plus de 65000, ce qui permet dinclure non
seulement les caractres accentus ou spcifiques certaines langues europennes, mais galement
tous les idogrammes des langues orientales. Pour faciliter le passage de la reprsentation ASCII
la reprsentation UNICODE, cette dernire est telle que les caractres du rpertoire ASCII sont
reprsents par les 7 bits de la reprsentation ASCII en poids faible, complts par des 0 en poids
forts :
0 0 0 0 0 0 0 0
UNICODE

0 b6 b5 b4 b3 b2 b1 b0
ASCII

Java reprsente les caractres selon le standard UNICODE.

15.3.2

Reprsentation des entiers positifs : binaire


La reprsentation usuelle des nombres entiers positifs (N) est la reprsentation en base 2, ou binaire.
Les nombres sont reprsents sur un nombre fix de bits, ce qui limite lintervalle reprsentable. Sur

1. ASCII est le sigle de American Standard Code for Information Interchange

258

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Reprsentation des informations dans les ordinateurs

n bits, on reprsente en binaire lintervalle dentiers [0, 2n-1].


Les tailles usuelles sont :

8 bits : reprsentation des entiers de 0 255,


16 bits : reprsentation des entiers de 0 65 535
32 bits : reprsentation des entiers de 0 4 294 967 295
64 bits : reprsentation des entiers de 0 264-1, soit environ 161018

15.3.3

Reprsentation des entiers relatifs : complment 2


Une mthode directe pour reprsenter les nombres entiers relatifs (Z) consisterait utiliser un bit
pour indiquer le signe. Par exemple, sur 8 bits, on peut utiliser les 7 bits b6...b0 pour reprsenter la
valeur absolue du nombre et le bit 7 pour reprsenter le signe, selon la convention : b7=0 si le nombre est positif, b7=1 si le nombre est ngatif. Ainsi :

0 000 0101 reprsente 5


1 000 0101 reprsente -5
Cette reprsentation, appele valeur absolue plus signe, nest pratiquement pas utilise. Bien
quelle soit facile comprendre, elle prsente quelques inconvnients. Notamment, la reprsentation de 0 nest pas unique, ce qui pose quelques difficults pour les tests dgalit des nombres. En
effet, 0 peut tre reprsent par 0000 0000 ou par 1000 0000.
On lui prfre la reprsentation suivante, appele complment 2, qui offre de nombreux avantages
et de ce fait a t adopte trs tt sur tous les ordinateurs.

pour un nombre positif, de 0 2n-1-1, la reprsentation en complment 2 est gale la repr-

sentation binaire,
pour un nombre ngatif, de -2n-1 -1, la reprsentation en complment 2 de k est la reprsentation binaire de 2n-|k|.
exemple sur 4 bits

nombre complment 2
-8
1000
-7
1001
-6
1010
-5
1011
-4
1100
-3
1101
-2
1110
-1
1111
0
0000
1
0001
2
0010
3
0011
4
0100
5
0101
6
0110
7
0111

interp. binaire

8
9
10
11
12
13
14
15
0
1
2
3
4
5
6
7

En complment 2, le bit de rang n-1 (bit de poids fort) est significatif du signe :

Si xn-1=0, le nombre reprsent est positif et il vaut, comme en binaire, xn-2.2n-2+...+ x1.2 + x0
Si xn-1=1, le nombre reprsent est ngatif et il vaut -2n-1 +xn-2.2n-2+...+ x1.2 + x0
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

259

Reprsentations usuelles des types simples

Pour cette raison on a coutume dappeler bit de signe le bit de poids fort dune suite de bits.
Une formule quivalente permtant dinterprter en complment 2 une suite de bits est :
interprtation en complment (xn-1xn-2....x1.x0) = -xn-12n-1 +xn-2.2n-2+...+ x1.2 + x0
cette formule considre simplement le chiffre de poids fort avec un poids ngatif.
Les avantages de cette reprsentation sont lunicit de la reprsentation de 0 et surtout que le mme
oprateur matriel daddition fonctionne aussi bien pour la reprsentation binaire simple que pour
la reprsentation en complment 2. Ceci est quasiment miraculeux : un seul oprateur, cest--dire
un seul algorithme, donne le bon rsultat pour les deux interprtations de ces suites de bits.
Exemple sur 4 bits :
opration machine

interprtation binaire

interprtation en complment 2

1001
+ 0011

9
+3

-7
+3

1100

12

-4

Sans chercher faire la dmonstration de cette proprit remarquable (ce genre de dmonstration
est assez pnible suivre, et sans grand intrt), on peut signaler quil sagit l de proprits arithmtiques des classes de congruence modulo 2n.

Dpassements de capacit
En fait, au lieu de dire que le mme oprateur marche aussi bien pour les deux reprsentations, on
devrait plutt dire quil ne marche pas plus mal pour lune que pour lautre. En effet, dans chacune des reprsentations il se pose le problme de dpassement de capacit : la somme peut sortir
de lintervalle de reprsentation. Bien videmment dans ce cas, le rsultat ne peut pas tre correct
puis quil nest pas reprsentable sur n bits.
Cest propos des dpassements de capacit que loprateur daddition diffre. Les conditions de
dpassement ne sont pas les mmes dans les deux reprsentations.

Pour la reprsentation binaire, le dpassement de capacit a lieu lorsque la somme des nombres
reprsents par les oprandes est suprieure ou gale 2n. Cette condition est dtecte sur la
machine par le fait que lalgorithme daddition binaire gnre un report 1 dans laddition des
chiffres de poids fort (carry en Anglais).

Pour la reprsentation en complment 2, le dpassement de capacit a lieu lorsque la somme

des nombres reprsents par les oprandes est soit strictement infrieure -2n-1, soit suprieure
ou gale 2n-1. Cette condition sappelle un dbordement (overflowen anglais). Elle peut tre
dtecte sur la machine de deux faons :
Premire faon : les oprandes sont de mme signe et le rsultat de lopration a un bit de signe
diffrent. De toute vidence le rsultat est incorrect dans ce cas. Ce qui est plus difficile cest de
montrer que ce sont l les seuls cas de dpassement.

260

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Reprsentation des informations dans les ordinateurs

Deuxime faon : la condition de dbordement correspond galement au cas o les reports


gnrs par les chiffres de rang n-1 et de rang n sont diffrents.
Les calculs suivants sur 4 bits montrent :

gauche, un cas de rsultat correct en interprtation binaire et incorrect en complment 2,


droite, un cas de rsultat incorrect en interprtation binaire et correct en complment 2.
binaire complment 2

binaire complment 2

0110

+6

1110

14

-2

0011

+3

1011

11

-5

1001

-7

1001

-7

report = 0

correct

dbordnt=1

report = 1
incorrect

incorrect

dbordnt=0

correct

Voici quelques rgles (sans dmonstration) concernant les calculs en complment 2 :


Passage la reprsentation de loppos :
Connaissant la reprsentation du nombre k, on dsire connatre celle de -k.
Il suffit de changer tous les 0 en 1 et les 1 en 0, puis effectuer lalgorithme daddition de 1 au rsultat. Par exemple, sur 4 bits, 0100 est la reprsentation de +4. La reprsentation de -4 sobtient en
inversant les bits puis en ajoutant 1 :
0100

1011
+ 1
1100, reprsentation de -4

Cette proprit peut se justifier ainsi : si on considre la somme des interprtations en complment
2 dune suite de n bits xn-1...x1 x0 et de son complment bit bit /xn-1.../x1 /x0 on a :
interprtation(xn-1...x1 x0) + interprtation(/xn-1.../x1 /x0) = 111...11 = -2n-1 +2n-2+...+ 2 +1 = -1

donc : interprtation(/xn-1.../x1 /x0) +1 = -interprtation(xn-1...x1 x0)


Une autre faon de procder consiste inverser les bits de poids suprieur au premier bit 1 rencontr partir des poids faibles : 0100 1100

Augmentation du nombre de bits :


Connaissant la reprsentation du nombre k sur n bits, on dsire connatre sa reprsentation sur n+p
bits.
Il suffit de procder une extension du bit de signe, cest--dire concatner en poids forts p bits
gaux au bit de poids fort de la reprsentation sur n bits. Exemples :
+5 est reprsent par 0101 sur 4 bits, sa reprsentation sur 8 bits est 0000 0101,
-5 est reprsent par 1011 sur 4 bits, sa reprsentation sur 8 bits est 1111 1011,

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

261

Reprsentations usuelles des types simples

Reprsentations de 0 et de -1 :
Quelque soit le nombre de bits, la reprsentation de 0 est de la forme 000...0 (que des 0) et celle de
-1 est 111...1 (que des 1).
En Java il y a quatre types dentiers relatifs :
byte : sur 8 bits, reprsente lintervalle -128...+127,
short : sur 16 bits, reprsente lintervalle -32 668...+32 767,
int : sur 32 bits, reprsente lintervalle -2 147 483 648...+2 147 483 647,
long : sur 64 bits, reprsente lintervalle
-9 223 372 036 854 775 808 ...+9 223 372 036 854 775 807.
En Java, comme dans de nombreux autres langages de programmation, les oprations +, -, *, /
sont celle de larithmtique modulo 2n, n tant le nombre de bits de la reprsentation. Ceci signifie
que les dpassements de capacit ne provoquent pas de dtection derreur pendant lexcution mais
donnent le rsultat modulo 2n de lopration, compris entre -2n-1 et 2n-1-1.
Par exemple, pour le type byte, reprsent sur 8 bits :
lopration 127+1 donne -128, et 127+2 donne -1.

15.3.4

Reprsentation des nombres rels


Quelle que soit la reprsentation adopte, un nombre fini de bits ne pourra reprsenter quun nombre fini de valeurs. Pour les entiers cela se traduit par limposition de valeurs limites infrieures et
suprieures de lensemble des valeurs reprsentes. Pour les rels cela se traduit en plus par une
prcision limite de reprsentation des valeurs dun intervalle.
Les reprsentations de nombres rels les plus utilises sont les reprsentations en virgule flottante,
ou plus simplement flottante (floating pointou floaten anglais).
Principe gnral
Le principe gnral dune reprsentation flottante consiste utiliser deux reprsentations dentiers :

la mantisse m qui apporte la prcision,


lexposant e qui indique lordre de grandeur du nombre.
Le nombre rel reprsent par ces deux entiers est : m be
o b est une constante, gnralement gale la base choisie pour la reprsentation de m.
En base 10, comme sur les calculettes, b=10. Sur ordinateur, en rgle gnrale b=2 car la mantisse
est reprsente en binaire. Le fait que b soit la base de reprsentation de m facilite certains traitements, notamment pour reprsenter le mme nombre en changeant dexposant : il suffit alors de
dcaler convenablement les chiffres de la mantisse.
Exemple, en base 10 : 356710-4 = 35670010-6
Intrt des reprsentations flottantes
Les reprsentations flottantes permettent une prcision relative peu prs constante sur toute la
262

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Reprsentation des informations dans les ordinateurs

gamme de reprsentation ainsi quune gamme tendue.


La prcision relative dun nombre x reprsent avec une incertitude x est x/x. De toute vidence,
si un rsultat de calcul est donn avec une mantisse de n chiffres significatifs (sans 0 en poids forts),
la prcision relative du rsultat de cette opration est x/x<1/bn.
Notion de reprsentation normalise
Plusieurs reprsentations flottantes sont possibles pour un mme nombre. Par exemple, en reprsentation dcimale de la mantisse sur 5 chiffres :
0049810 -5 = 4980010 -7
Ceci prsente deux inconvnients : le manque dunicit de la reprsentation et, surtout, la perte
potentielle de prcision si on autorise des 0 non significatifs en poids fort de la mantisse. Pour cela,
on simpose dutiliser une reprsentation dite normalise, dans laquelle le chiffre de poids fort de la
mantisse est diffrent de 0.
Par exemple, pour reprsenter 498592 avec seulement 5 chiffres pour la mantisse :
-5

avec la reprsentation normalise 4985910+1 lerreur relative est de lordre de 10


-2
avec la reprsentation non normalise 0004910+4 lerreur relative est de lordre de 10
Flottant standard IEEE
Les nombres rels sont dune grande importance pour les applications de calcul scientifique. Afin
dassurer que les mmes calculs donnent les mmes rsultats sur toutes les machines, un standard
t dfini : il sagit du standard IEEE 754 qui dfinit deux reprsentations, la reprsentation flottante simple prcision et la reprsentation flottante double prcision. Ce standard dfinit non seulement le format de reprsentation mais galement le comportement des oprations.
Flottant IEEE simple prcision : la reprsentation occupe 32 bits, un bit pour le signe du nombre,
8 bits pour lexposant et 23 bits pour la mantisse.

signe
S

31 30

8 bits
exposant
E

23 bits
mantisse
M

23 22

Les 23 bits de mantisse sont lquivalent denviron 7 chiffres dcimaux, ce qui donne une prcision
relative d'environ 10-7, ce qui est peu (la moindre calculette est gnralement plus prcise).
Lexposant, reprsent sur 8 bits, peut prendre des valeurs comprises entre -126 et +127, ce qui
offre une tendue de 2127 1038
Flottant IEEE double prcision : la reprsentation occupe 64 bits, un bit pour le signe du nombre,
11 bits pour lexposant et 52 bits pour la mantisse.

signe
S

63 62

11 bits
exposant
E

52 bits
mantisse
M
52 51

Les 52 bits de mantisse sont lquivalent denviron 16 chiffres dcimaux, ce qui donne une prcision relative d'environ 10-16.
Lexposant, reprsent sur 11 bits, peut prendre des valeurs comprises entre -1022 et +1023, ce qui
offre une tendue de 21023 10308

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

263

Reprsentations usuelles des types simples

Interprtation flottante
Voici plus prcisment comment fonctionne la reprsentation, pour le cas du flottant simple prcision (cest le mme principe pour le double prcision en changeant les nombres de bits).
Pour une reprsentation normalise :
lexposant E est interprt en un entier e [-126,+127] selon la formule :
e = interprtation binaire(E)-127
Cette reprsentation particulire des entiers relatifs sappelle reprsentation par excs 127.
La mantisse M est interprte en un nombre fractionnaire m [1, 2 [ selon la formule :
m = 1 + interprtation binaire(M) / 2

23

cest--dire en un nombre qui en criture binaire fractionnaire scrirait : 1,M22M21...M1M0


Le 1, chiffre de poids fort de la mantisse, est implicite, il ne figure pas dans la mantisse. Il y a l une
astuce qui ne marche quen binaire : puisque la mantisse est normalise, son chiffre de poids fort est
diffrent de 0, il vaut donc 1 et ce nest pas la peine de le noter (conomie dun bit).
Enfin, linterprtation flottante des 32 bits formant le triplet <S,E,M> est :
interprtation flottante<S,E,M> = (-1)S m 2

Le standard IEEE prvoit galement :

La reprsentation de nombres non normaliss, pour reprsenter de tous petits nombres mais
avec une prcision relative dgrade.
La reprsentation de 0 : le rel 0 est reprsent par 0000......00.
La reprsentation de valeurs spciales : les infinis signs, les cas derreurs (appels NaNs abrviation du terme anglais Not a Number, cest--dire un non-nombre).

Toutes ces reprsentations spciales sont distingues des reprsentations normalises car elles ont
un exposant E de la forme 00000000 ou 1111111, formes non utilises pour les reprsentations normalises.
Voici quelques exemples :
reprsentation flottant
IEEE simple prcision

nombre
1 = +1.020= +1.0 2127-127
0.5 =

+1.02-1=

+1.0

2126-127

4 = +1.022= +1.0 2129-127


0.75 =

+1.52-1=

+1.5

2126-127

0 01111111 0000...0
0 01111110 0000...0
0 10000001 0000...0
0 01111110 1000...0

4.5 = +1.12522= +1.125 2129-127

0 10000001 0010...0

2128-127

0 10000000 0110...0

2.75 = +1.3752=+1.375

Java offre les deux sortes de reprsentations de rel :

float : flottant IEEE simple prcision,


double : flottant IEEE double prcision,
264

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Reprsentation des informations dans les ordinateurs

15.4

Reprsentation des types composites : structures, objets, tableaux


Les types que nous avons vus prcdemment sont des types simples, encore appels types scalaires.
En rgle gnrale, les valeurs de types scalaires sont manipulables directement et dans leur totalit
par les processeurs : les processeurs disposent dinstructions pour raliser les oprations et des
registres pour contenir les oprandes et rsultats de calcul.
Pour les types composites, qui sont des assemblages de plusieurs donnes, les processeurs usuels ne
savent pas les traiter directement dans leur totalit. La reprsentation des types composites utilise la
mmoire de lordinateur, son accs par adresses et les possibilits de calcul dadresses du processeur.

15.4.1

Reprsentation des structures et des objets de type classe


Les donnes dun objet de type classe sont ranges en mmoire des adresses conscutives. La
figure suivante illustre la reprsentation en mmoire dun objet de type Robot , appel vigor,
rang en mmoire partir de ladresse adresseVigor. Ses donnes sont constitues :

dun int orientation, 32 bits ou 4 octets, situ ladresse adresseVigor+0,


dun double ,X64 bits ou 8 octets, situ ladresse adresseVigor+4,
dun double , Y64 bits ou 8 octets, situ ladresse adresseVigor+12.
Si, par exemple, adresseVigor vaut 1000, on a le schma suivant :
vigor
class Robot {
int orientation;
double X
double Y...
}

1000
adresseVigor + 0
1000

orientation

adresseVigor + 4
1004

adresseVigor + 12
1012

Comme le montre cet exemple, chaque composant de lobjet correspond un dplacement, en


nombre doctets, partir de ladresse de dbut de lobjet. Ladresse de lobjet est la rfrence
lobjet : cest cette adresse qui constitue la valeur des variables, des paramtres ou des rsultats de
ce type. Cest galement la valeur du paramtre implicite this lors dun appel de mthode dun
objet de ce type.
Le compilateur traduit chaque composant non statique dune classe en un dplacement : dplacement 0 pour orientation , dplacement 4 pour X et dplacement 12 pour Y. Pendant lexcution
dune mthode dun objet de ce type, pour atteindre un composant de lobjet le processeur devra
ajouter le dplacement correspondant ladresse de lobjet.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

265

Reprsentation des types composites : structures, objets, tableaux

15.4.2

Reprsentation des tableaux


La reprsentation des tableaux utilise avec profit le fait que la mmoire dun ordinateur se comporte
comme un tableau doctets indic par les adresses. Un tableau est reprsent par une suite demplacement conscutifs partir dune adresse de dbut :
taille du tableau
int[] musique = new int[2400000];
musique

adresseMusique - 4

2400000

musique.length

adresseMusique + 0

musique[0]

adresseMusique + 4

musique[1]

adresseMusique + 8

musique[2]

adresseMusique +
4 2399999

musique[2399999]

Lexemple illustre un tableau appel musique de 2400000 lments de type int. Chaque lment
occupe 32 bits, soit 4 octets. Le dbut du tableau, qui correspond au premier lment musique[0] , est plac ladresse adresseMusique. Chaque lment musique[ i] est place
ladresse adresseMusique+4i, le facteur 4 tant la taille de reprsentation du type int en octets.
Dans un langage comme Java, la taille dun tableau est indique au moment de sa cration. La taille
doit tre note en association avec le tableau, en un endroit que lexcuteur du programme puisse
facilement retrouver. La taille peut par exemple tre place en mmoire juste avant le tableau,
ladresse dbut du tableau- 4 si la taille est un entier sur 32 bits.

266

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Reprsentation des informations dans les ordinateurs

15.5
15.5.1

Oprations sur les rels - erreurs


Addition - soustraction
Ces oprations sont ralises selon la mthode suivante :

alignement des exposants sur le plus lev,


addition des mantisses,
normalisation du rsultat et arrondi.
Pour rendre les exemples comprhensibles, nous les donnons en dcimal. Le principe est bien videmment le mme en binaire, par exemple avec la reprsentation IEEE. Nous prenons 4 chiffres
dcimaux pour la mantisse et 2 chiffres dcimaux pour lexposant.
4.832 103 + 2.104 100
4.832
103
+ 0.002104 103
4.834104 103
larrondi ne conserve que 4 chiffres pour la mantisse : 4.834
4.832 101 - 4.827 101
4.832
- 4.827
0.005
normalis en

15.5.2

101
101
101

10-2

5.000

Multiplication - Division
Ces oprations sont ralises selon la mthode suivante :

addition (soustraction) des exposants,


multiplication (division) des mantisses,
troncature/normalisation.
Exemples
1.564 100

1.589 100
2.485196 100

15.5.3

2.0 101
arrondi en 2.485

3.0 100
6.6666666 100 arrondi en 6.666

Erreurs dans les calculs sur les rels


Les erreurs lors des calculs sur des nombres rels constituent un des problmes les plus importants
et les plus difficiles du calcul scientifique. Chaque opration peut introduire des erreurs darrondis
cause du nombre de chiffres limits de la mantisse. La dtermination du degr de confiance que lon
peut attribuer aux rsultats est gnralement bien plus compliqu que les algorithmes qui calculent
ces rsultats.

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

267

Oprations sur les rels - erreurs

15.5.3.1 Erreurs lors des multiplications et des divisions


Lerreur introduite lors dune multiplication ou dune division est assez facile matriser :

Lerreur relative due lopration elle-mme est majore par la valeur reprsente par lunit du

chiffre de poids faible de la mantisse (1/bk pour k chiffres en base b).


En ce qui concerne la propagation des erreurs, cest--dire lerreur sur le rsultat d aux incertitudes sur les oprandes, les erreurs relatives sur les oprandes sajoutent :
x.y = (x + x) (y + y) = x.y + x.y ( + + .) soit, en ngligeant . : = x.y + (1 + +)

15.5.4

Erreurs lors des additions et des soustractions


Les erreurs lies ces deux oprations sont plus difficiles formaliser et peuvent avoir des consquences spectaculaires. Ces erreurs sont dues deux phnomnes : labsorption et llimination.
Erreurs dlimination
Ce phnomne se produit lorsquon soustrait des nombres trs voisins. Les chiffres de poids forts
sliminent et il faut alors normaliser le rsultat en rajoutant des zros en poids faible. Or, les oprandes sont en gnral des rsultats arrondis doprations antrieures et lutilisation des oprandes
exacts feraient apparatre des chiffres la place de ces zros. La valeur calcule peut alors tre trs
diffrente de la valeur exacte.
Lerreur relative est trs souvent de lordre de 100%, ce qui revient dire que le rsultat est le fruit
du hasard.
Exemple dlimination :
X = 1.405 103
-

Y = 1.404 103
0.001 103

Ce qui donne aprs normalisation : 1.000 100


Si X et Y rsultent de calcul et sont les arrondis de :
X = 1.405456 et Y= 1,404012, le rsultat exact est 1.444.
Lerreur relative est ici de 40%.
Erreurs dabsorption
Les erreurs dabsorption se produisent lors de laddition ou de la soustraction de deux nombres de
grandeurs trs diffrentes. Ceci est d la perte de chiffres lors de lalignement des exposants. On
peut mme perdre tous les chiffres significatifs du plus petit des nombres de sorte que le rsultat est
simplement le plus grand des deux.
Exemple dabsorption :
1.232 x 104 + 2.104 x 101
1,232

104

+ 0.002404 104
1.234404 104
Ce qui aprs arrondi donne 1.234 104
268

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Reprsentation des informations dans les ordinateurs

Autre exemple dabsorption (catastrophique) : soit calculer 1.6 + 109 - 109


Si on effectue le calcul selon lordre induit par le parenthsage suivant : 1.6 + (109 - 109)
on obtient : 1.6 + 0 = 1.6, ce qui est normal.
Mais si on leffectue selon lordre correspondant : (1.6 + 109) - 109
on obtient : 109 - 109= 0, car par arrondi 1.6 + 109= 109
Cet exemple montre que laddition avec les flottants est une opration non associative !
Exemple dalgorithme sensible labsorption : calcul de la somme de la srie harmonique
n

S =

--i-

i=1

Sommation par ordre dcroissant des termes :


double S=0;
for (int i=1; i<=n; i++) {S=S+1/i;}
On commence par les grands termes. Les plus petits termes sont absorbs par la somme partielle
dj calcule.
Sommation par ordre croissant des termes :
double S=0;
for (int i=n; i>=1; i--) {S=S+1/i;}
Cette fois les plus petits termes ne sont pas absorbs, le rsultat est meilleur.
n

10 000

100 000

10 000 000

10 000 000

par ordre dcroissant des termes

9.787613

14.35736

15.40368

15.40368

par ordre croissant des termes

9.787604

14.39265

16.68603

18.80792

valeur exacte

9.787606

14.39273

16.69531

18.99789

Exemple : Rsolution dune quation du second degr


Considrons lquation x2 - 205 x + 10500 = 0
Les deux racines relles sont 100 et 105.
Avec une reprsentation dont les mantisses ont 3 chiffres, les rsultats seront faux :
= (2.05 102) 2 - 4 x 1.05 104
= 4.20 104 - 4.20 104
=0

=> x1 = x2 = 100

En revanche, avec 5 chiffres de mantisse, les rsultats sont corrects :


= (2.05 102)2 - 4 x 1.05 104
= 4.2025 104 - 4.2000 104
= 0.0025 104
= 25

=> x1 = (205-5) / 2 = 100

x2 = (205+5) / 2 = 105

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

269

Oprations sur les rels - erreurs

270

Module A.P.I

Algorithmique Programmation Imprative

Universit de Rennes 1

Annexes

Installations

ANNEXE 1

1.1

Installation de Java
On peut tlcharger Java depuis le site de Sun Micro-System :
http://java.sun.com/j2se/1.5.0/download.jsp
le lien ci-dessus est la version de dbut 2006. Bien videmment il convient de prendre la plus
rcente version du moment. Le site propose une installation pour la plupart des systmes : Windows, Linux, Mac-OS, Sun-OS. Le fichier tlcharg est un programme dinstallation quil suffit
dexcuter.

1.2

Test de linstallation de java


Pour tester si java est correctement install, on peut essayer le programme simple suivant :
class Test1{
public static void main(String [] arg){
System.out.println("un test");
}
}
Pour cela introduire ce programme dans un fichier texte Test1.java , au moyen dun diteur de
texte brut (par exemple bloc-notes ou Xemacs).
Pour compiler le programme, il faut pouvoir appeler le compilateur. Dans ce premier test, nous le
ferons par une commande tape dans une fentre de commande. On peut solliciter le compilateur
par son chemin absolu. Par exemple, pour une installation Windows :
>"c:\Program Files\jdk1.5.0_06\bin\javac" Test1.java
Pour disposer plus simplement des commandes javac et java, il est prfrable de rajouter le chemin
c:\Program
Files\jdk1.5.0_06\bin la variable denvironnement path. On peut
alors lancer la compilation par :
> javac Test1.java
Si le compilateur ne trouve pas derreur, il doit produire le fichier Test1.class excutable par
linterprteur java. Pour lexcuter il faut taper la commande :
> java Test1
Lexcution doit produire laffichage :
> un test
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

271

Annexes

1.3

Ajout de paquetages
Pour faire certains exercices, nous utiliserons des classes regroupes dans des paquetages (package). Ces paquetages se trouvent dans le rpertoire paquetagesMaison . Voici ce quoffrent
ces paquetages :
es : lecture de donnes depuis le clavier et depuis des fichier, criture de donnes sur des fichiers,
list : listes de donnes de types quelconque,
ens : ensembles de valeurs de divers types,
ihm : placement de composants graphiques.
Chaque paquetage est un rpertoire qui contient des classes destines tre utilises dans des applications. Pour utiliser un tel paquetage, il faut en dbut du texte du programme, placer une directive :
import nomDuPaquetage.*;
Le programme suivant utilise la procdure Lecture.unEntier du paquetage es pour lire des
donnes frappes au clavier.
import es.*;
class Test2 {
public static void main(String [] arg){
System.out.print("entrer deux nombres
int nombre1=Lecture.unEntier();
int nombre2=Lecture.unEntier();
System.out.print("leur somme = ");
System.out.println(nombre1+nombre2);
}
}

:");

Ce programme lit deux nombres entiers, frapps au clavier, en dcimal, spars par un espace ou
par des passages la ligne, puis il affiche leur somme.
Si on essaye de le compiler comme prcdemment, le compilateur indique une erreur car il ne connat pas la classe Lecture . Il faut en plus lui indiquer le rpertoire o se trouve le paquetage es.
On peut le lui indiquer :

Soit grce la variable denvironnement CLASSPATH : la positionner en lui indiquant le chemin du rpertoire paquetagesMaison ainsi que le rpertoire courant, not .. Sur un systme Windows, la commande est
set CLASSPATH=.../paquetagesMaison;.
.../paquetagesMaison dnote ici le chemin absolu du rpertoire paquetageMaison
(cela dpend de lendroit o le dossier a t plac).
Soit en lanant la commande de compilation avec loption -classpath :
javac -classpath
.../paquetagesMaison;. Test2.java
Dans ce cas, la commande dexcution doit galement se faire avec cette option :
java -classpath
.../paquetagesMaison;. Test2

Le programme Test2 attend lintroduction de deux nombres entiers au clavier, termin par un passage la ligne, et affiche leur somme :
entrer deux nombres
leur somme = 60

: 45 25

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

272

Annexes

1.4

Installation dEclipse
Eclipse est un outil de dveloppement sophistiqu pour de nombreux langages (langages de programmation, principalement pour Java, mais aussi C++, ou bien langages documentaires tels
HTML, XML...). On peut tlcharger Eclipse depuis le site :
http:download.eclipse.org/eclipse/dowloads
Il convient de charger la plus rcente version. En dbut 2006, pour du dveloppement Java, sur Windows, le fichier tlcharger est :
eclipse-SDK-3.1.2.win32.zip
Il existe des installation pour quasiment tous les autres systmes : Linux, Unix-Solaris, MacOS.
Il sagit simplement dun dossier compress appel eclipse quil suffit de dployer, par exemple,
sous windows, dans C:/Programm
File
. Sous Windows, on le lance en excutant
eclipse.exe qui est dans le dossier eclipse.

1.5

Ouverture dun projet Eclipse


Avec Eclipse, les applications Java sont organiss en projets. Pour de simples exercices, on peut
les raliser dans un mme projet.
Commencer par crer le rpertoire du projet, par exemple c:\ProjetCoucou (on peut ventuellement y placer des programmes sources java que lon possderait dj. Ils seront pris en compte
dans le projet).
Par les menus dEclipse :

File - New - Project


choisir Java Project , next
donner le nom du projet dans Project name : ProjetCoucou
slectionner Create project at external location
indiquer le rpertoire du projet (browse) : c:\ProjetCoucou
next
Si on veut utiliser des paquetages autres que ceux de la bibliothque standard, par exemple les
paquetages maison, onglet Librairies :
ajouter par Add External JARs (browse) :
...\paquetagesMaison.jar
ouvrir, finish.

A la question voulez vous passer tout de suite la perspective Java ? rpondre yes
La perspective Java utilise essentiellement 3 fentres :

La fentre dexploration du projet ( gauche). Les classes du projet sont visibles dans la rubri-

que default package .


La fentre ddition des programmes source Java (au milieu en haut).
La fentre (au milieu en bas) qui sert aux messages derreur de compilation (onglet Problems )
et en tant que console dexcution (onglet Console ).

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

273

Annexes

Les fentres inutiles senlvent en cliquant sur les croix qui leur correspondent. Les fentres se
raffichent par Window - Show View .
File...
Package Explorer

Coucou.java

ProjetCoucou
default package

fentre ddition

class Coucou {
...
}

fentre
dexploration
du projet

Problems

Console

messages derreur de compilation

fentre dexcution

Suppression dun projet :


slectionner le projet dans la fentre dexploration et : File delete
(attention : laisser loption par dfaut ne pas dtruire le contenu
de faon simplement retirer le
projet dEclipse sans dtruire les fichiers).
En cas de panique :
Pour repartir dans un tat vierge, supprimer les fichiers .project et .classpath du rpertoire
du projet.

1.6

Cration dune classe


Menu : File - New - Class
Name : Coucou
Pour une classe excutable (dot dune procdure main) choisir
public static void main(String[] arg)
finish
Compilation :
la compilation de tout ce qui doit tre compil se fait chaque sauvegarde (menu File Saveou
icne disquette dans le bandeau).

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

274

Annexes

1.7

Excution
Slectionner la fentre ddition du programme excuter (la classe qui contient le main) puis :
Run - Run as - Java application
Pour communiquer avec le programme ou voir les messages derreur dexcution, slectionner
longlet Console sur la fentre du bas.
Arrt forc de lexcution :
(par exemple pour arrter un programme qui boucle indfiniment)
en haut de la fentre Console , cliquer sur le carr rouge.
Pour en savoir plus...
Lire le petit manuel lusage du dveloppeur Java sous Eclipse
de Yves Bekkers - Ifsic

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

275

Annexes

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

276

Annexes

ANNEXE 2

Paquetages pour les exercices

Nous donnons ici les programmes sources des paquetages maison utiliss pour les exercices et les
travaux pratiques. Ce sont :
Paquetage es :
classe Lecture : entres depuis le clavier.
classe LectureFichierTexte : entres depuis un fichier texte.
classe EcritureFichierTexte : sorties sur un fichier texte.
Paquetage list :
classe Liste<T> : listes gnriques
La classe ListeDEntiers en est une spcialisation :
class ListeDEntiers extends Liste<Integer>
Paquetage ens :
classe Ensemble<T> : ensembles gnriques
La classe EnsembleDEntiers en est une spcialisation :
class EnsembleDEntiers extends Ensemble<Integer>

2.1

Entres-sorties clavier et fichiers textes


package es;
import java.io.*;
public class Lecture { // lectures clavier
public static char unCar() {
// effet
: lit un caractre frapp
// rsultat
: le caractre frapp
char c;
try {c=(char) System.in.read();}
catch(IOException e) {c=(char) 0;};
return c;
}
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

277

Annexes

public static String chaine(String delimiteurs) {


// prrequis
: delimiteurs.length()!=0
// effet
: lit une squence de caractres constitue
// de dlimiteurs puis de caractres autres que des dlimiteurs.
// les dlimiteurs sont les caractres de dlimiteurs.
// rsultat
: la chaine forme des caractres compris entre
// les dlimiteurs
StringBuffer b = new
StringBuffer();
char c=unCar();
// ignore les dlimiteurs de tte
while (delimiteurs.indexOf(c)!=-1) {c=unCar();};
// lit jusquau prochain dlimiteur
while (delimiteurs.indexOf(c)==-1) {b.append(c); c=unCar();};
return b.toString();
}
public static String chaine() {
// rsultat
: lecture dune chane avec pour dlimiteurs
// lespace et le passage la ligne
return
chaine(" \r\n");
}
public static int unEntier() {
// effet
: lit une chane de caractres avec pour dlimiteurs
// lespace et le passage la ligne
// rsultat
: lentier represent en decimal par cette chane
// si la chane ne respecte pas la syntaxe dun entier dcimal,
// affiche un message derreur et retourne 0.
String s=Lecture.chaine(" \r\n");
try { return Integer.parseInt(s);}
catch(NumberFormatException e) {
System.err.println("\nErreur lecture dentier");
System.err.println("valeur 0 retourne");
return 0;
}
}

public static double unReel() {


// effet
: lit une chane de caractres avec pour dlimiteurs
// lespace et le passage la ligne
// rsultat
: le flottant represent par cette chane
// si la chane ne respecte pas la syntaxe pour un nombre rel,
// affiche un message derreur et retourne 0.
String s=Lecture.chaine(" \r\n");
try { return
(Double.valueOf(s)).doubleValue();}
catch(NumberFormatException e) {
System.err.println("\nErreur lecture de rel");
System.err.println("valeur 0 retourne");
return 0;
}
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

278

Annexes

package es;
import java.io.*;
public class LectureFichierTexte {
private
private
private
private

BufferedReader leFichier;
char prochainCaractere;
boolean finDeFichier;
String nom;

private void tenteDeLireProchainCaractere() {


try {
int x = leFichier.read(); prochainCaractere = (char) x;
if (x==-1) {finDeFichier = true;}
}
catch(IOException e){
System.err.println("erreur de lecture du fichier "+nom);
Thread.dumpStack();
}
}
public LectureFichierTexte(String nom) {
// initialise un accs en lecture sur le fichier de nom nom
// erreur si le fichier nexiste pas
this.nom=nom;
try {
leFichier = new BufferedReader(new FileReader(nom));
finDeFichier = false; tenteDeLireProchainCaractere();
}
catch(FileNotFoundException e){
System.err.println("fichier "+nom+" inexistant");
Thread.dumpStack();
}
}
public void fermer() { // fermeture du fichier
try {leFichier.close();}
catch(IOException e) {
System.err.println(
"erreur lors de la fermeture du fichier "+nom);
Thread.dumpStack();
}
}
public char lireUnCar() { // lecture dun caractre
char courant = prochainCaractere;
tenteDeLireProchainCaractere();
return courant;
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

279

Annexes

public String lireChaine(String delimiteurs) {


// lecture
dune chaine
comprise
entre dlimiteurs
// fin du fichier. Rend la chaine vide si fin de fichier.
if (finDeFichier()) {return "";}
char c=lireUnCar();
// ignore les delimiteurs de tte
while (!finDeFichier() && delimiteurs.indexOf(c)!=-1) {
c=lireUnCar();
}
if (finDeFichier()) {return "";}
// lit jusquau prochain dlimiteur ou fin de fichier
StringBuffer b = new StringBuffer(); b.append(c);
c=lireUnCar();
while (!finDeFichier() && delimiteurs.indexOf(c)==-1) {
b.append(c); c=lireUnCar();
}
if(delimiteurs.indexOf(c)==-1){b.append(c);}
// consomme les ventuels dlimiteurs suivants
while (!finDeFichier()
&& delimiteurs.indexOf(prochainCaractere)!=-1) {
c=lireUnCar();
}
return b.toString();
}

ou jusqu

public String lireChaine() {


// lecture dune chaine comprise entre delimiteurs standards
return lireChaine(" \r\n");
}
public int lireUnEntier() { // lecture dun entier
try { return Integer.parseInt(lireChaine());
}
catch(NumberFormatException e) {
System.err.println(
"Erreur lecture dentier sur fichier "+nom);
System.err.println("valeur 0 retourne");
return 0;
}
}
public double lireUnReel() { // lecture dun nombre rel
try { return (Double.valueOf(lireChaine())).floatValue();}
catch(NumberFormatException e) {
System.err.println(
"Erreur lecture de rel sur fichier "+nom);
System.err.println("valeur 0 retourne"); return 0;
}
}

public boolean finDeFichier() {


return finDeFichier;
}

// indique si fin de fichier

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

280

Annexes

package es;
import java.io.*;
public class EcritureFichierTexte {
private PrintWriter leFichier;
private String nom;
public EcritureFichierTexte(String nom) {
this.nom=nom;
try {leFichier = new PrintWriter(new FileOutputStream(nom));}
catch(IOException e){
System.out.println(
"erreur lors de la cration du fichier "+nom);
Thread.dumpStack();
}
}
public void ecrire(char c) {leFichier.print(c);}
public void ecrire(String s) {leFichier.print(s);}
public void ecrire(int k) {leFichier.print(k);}
public void ecrire(boolean b) {leFichier.print(b);}
public void ecrire(double x) {leFichier.print(x);}
}

2.2

public void fermer() {leFichier.close();}

Listes
La classe Liste prsente ici est une classe gnrique. Le type des lments (dsign par T) peut
tre nimporte quelle classe.
package list;
import parcours.*;
public class Liste<T> {
private static class Maillon<TE> {
Maillon<TE> suivant; Maillon<TE> precedent; TE element;
Maillon(Maillon<TE> s,Maillon<TE> p,TE e) {
suivant=s;precedent=p;element=e;
}
}
private Maillon<T> tete;
private Maillon<T> queue;
public Liste() { // liste vide
tete=null; queue=null;
}
Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

281

Annexes

public Liste(T[] e){ // liste contenant les lments de e


tete=null; queue=null;
for (int i=0;i<e.length;i++){ajouteEnQueue(e[i]);}
}
public boolean estVide() {
// rsulat
: indique si this est vide
return tete==null;
}
public String toString() {
// rsultat
: this en clair
if (estVide()) {return "<>";}
Parcours p= new Parcours();
StringBuffer resul =
new StringBuffer("< "+ p.elementCourant().toString());
p.suivant();
while (!p.estEnFin()) {
resul.append(" | " + p.elementCourant().toString());
p.suivant();
}
resul.append(" >"); return resul.toString();
}
public void ajouteEnQueue(T nouvelElement) {
// effet
: ajoute nouvelElement en queue de this
if (queue==null) { // cas liste vide
queue = new Maillon<T>(null,null,nouvelElement);
tete = queue;
}
else { // chaine en queue
Maillon<T> exDernier = queue;
queue = new Maillon<T>(null,exDernier,nouvelElement);
exDernier.suivant=queue;
}
}
public void ajouteEnTete(T nouvelElement) {
// effet : ajoute nouvelElement en tte de this
if (tete==null) { // cas liste vide
tete = new Maillon<T>(null,null,nouvelElement);
queue = tete;
}
else { // chaine en tete
Maillon<T> exPremier = tete;
tete = new Maillon<T>(exPremier,null,nouvelElement);
exPremier.precedent=tete;
}
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

282

Annexes

public T retireEnQueue() {
// prrequis
: this nest pas vide
// effet
: retire llment de queue
// rsultat
: llment retir
T resul = queue.element;
queue = queue.precedent;
if (queue!=null) {queue.suivant=null;}
else {tete = null;}
return resul;
}
public T retireEnTete() {
// prrequis
: this nest pas vide
// effet
: retire llment de tte
// rsultat
: llment retir
T resul = tete.element;
tete = tete.suivant;
if (tete!=null) {tete.precedent=null;}
else {queue = null;}
return resul;
}

public static <TE> Liste<TE> aPartirDe(TE[] e){


// rsultat
: une nouvelle liste contenant les lments de e
Liste<TE> resul=new Liste<TE>();
for (int i=0;i<e.length;i++){
resul.ajouteEnQueue(e[i]);
}
return resul;
}

public Liste<T>.Parcours nouveauParcours() {


// rsultat
: un nouveau parcours initialis au dbut de this
return new Parcours();
}
public Liste<T>.Parcours nouveauParcours (Liste<T>.Parcours p) {
// rsultat
: nouveau parcours initialis ltat de p
return new Parcours(p);
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

283

Annexes

//=================== parcoureur de liste =====================


public class Parcours implements ParcoursBidirectionnel<T>,
ParcoursModificateur<T>
private Maillon<T> courant;
public Parcours() {courant=tete;}
// Parcours initialis en tte
private Liste<T> laListe() { return Liste.this; }
public Parcours(Parcours p) {
// Parcours initialis avec ltat du Parcours p
if (p.laListe()!=Liste.this) {
System.out.println("erreur
:\n"
+ "parcours initialis avec celui dune autre liste");
Thread.dumpStack(); System.exit(0);
}
courant=p.courant;
}
public void tete() {
// effet
: positionne this en tete de liste
// (parcours en fin si liste vide)
courant=tete;
}
public void queue() {
// effet
: positionne this en queue de liste
// (parcours en fin si liste vide)
courant=queue;
}
public void suivant() {
// effet
: positionne this sur llment suivant,
// ne fait rien si estEnFin()
if (courant!=null) {courant=courant.suivant;}
}
public void precedent() {
// effet
: positionne this sur llment prcdent,
// ne fait rien si estEnFin()
if (courant!=null) {courant=courant.precedent;}
}
public boolean estEnFin() {
// rsultat
: indique si this est en fin
// (au dela de la queue, en dea de la tete)
return courant==null;
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

284

Annexes

public T elementCourant() {
// prrequis
: this nest pas en fin
// rsultat
: llment courant de this
return courant.element;
}
public void modifElement(T nouvelElement) {
// prrequis
: this nest pas en fin
// effet : remplace
llment
courant
de this
if (courant!=null) {courant.element=nouvelElement;}
}

par

nouvelElem

public void ajouteElement(T nouvelElement) {


// effet : insre
nouvelElement
l a suite de llment
// insre en tte si this est en fin de parcours
// llment insr devient llment courant
Maillon<T> suivant; Maillon<T> nouveau;
if (courant==null) { // chaine en tte
suivant=tete;
nouveau = new Maillon<T>(tete,null,nouvelElement);
tete=nouveau; courant=nouveau;
}
else { // chaine sur courant
suivant=courant.suivant;
nouveau = new Maillon<T>(suivant,courant,nouvelElement);
courant.suivant=nouveau; courant=nouveau;
}
if (suivant!=null) {suivant.precedent=courant;}
else { // nouvel element de queue
queue=courant;
}
}
public void retireElement() {
// prrequis
: this nest pas en fin
// effet
: retire llment courant de this
// le suivant
de llment
retir,
sil existe,
// courant, sinon this passe en fin de parcours
Maillon<T> suivant=courant.suivant;
Maillon<T> precedent=courant.precedent;
if (precedent!=null) {precedent.suivant=suivant;}
else {tete=suivant;}
if (suivant!=null) {suivant.precedent=precedent;}
else {queue=precedent;}
courant=suivant;
}

devient

}
//==============================================================

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

285

co

Annexes

2.3

Ensembles
La classe Ensemble prsente ici est une classe gnrique. Le type des lments (dsign par T)
peut tre nimporte quelle classe dot dune fonction de comparaison appele compareTo qui
induise une relation dordre.
package ens;
import parcours.*;
public class Ensemble<T extends Comparable<T>> {
private Object[] elements;

// tableau dlments de type T


// de taille ajuste aux besoins

private int nbElements;


private static final int facteurDeCroissance=2;
private static final int taillePourSingleton=1;
public Ensemble() { // ensemble vide
elements=null; nbElements=0;
}

public static <T extends Comparable<T>>


Ensemble<T> aPartirDe(T[] elts){
// rsultat
: un nouvel ensemble dentiers compos
// des lments de elts
Ensemble<T> resul = new Ensemble();
for (int i=0; i<elts.length; i++){
resul.ajouteElement(elts[i]);
}
return resul;
}
public int cardinal() {
// rsultat
: le nombre dlments de this
return nbElements;
}
public boolean estVide() {
// rsultat
: indique si this est vide
return nbElements==0;
}
public String toString() {
// rsultat
: this en clair
if (estVide()) {return "{}";}
StringBuffer resul = new StringBuffer("{ "+ elements[0]);
for (int i=1; i<nbElements; i++) {
resul.append(" , " + elements[i]);
}
resul.append(" }"); return resul.toString();
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

286

Annexes

private int indiceDe(T e) {


// rsultat
: indice de llment gal e, 0..nbElements-1,
// -1 si e est absent
int i = 0; int j = nbElements-1;
while(j>=i){
int m=(i+j)/2;
int comparaison = ((T) elements[m]).compareTo(e);
if (comparaison==0) {return m;}
else if (comparaison<0) {i=m+1;}
else {j=m-1;}
}
return -1;
}
public boolean contient(T e) {
// rsultat
: indique si e appartient this
return indiceDe(e)!=-1;
}
public void ajouteElement(T e) {
// effet
: ajoute e this (aucun effet si e est dj dans this)
// (aprs, e appartient this)
if (!contient(e)) {
if (elements!=null && nbElements<elements.length){
// place suffisante
int i=nbElements;
while (i>0 && ((T) elements[i-1]).compareTo(e)>0) {
elements[i]=elements[i-1]; i--;
}
elements[i]=e;
}
else { // place insuffisante, cration dun nouveau tableau
Object[] nouveau;
if (nbElements==0) {
nouveau = new Object[taillePourSingleton];
}
else {
nouveau=new Object[facteurDeCroissance*elements.length];
}
int i=nbElements;
while (i>0 && ((T) elements[i-1]).compareTo(e)>0) {
nouveau[i]=elements[i-1]; i--;
}
nouveau[i] = e; i--;
while (i>=0) { nouveau[i]=elements[i]; i--;}
elements=nouveau;
}
nbElements++;
}
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

287

Annexes

public void retireElement(T e) {


// effet : retire
e d e this (aucun
effet
// (aprs, e nappartient pas this)
int k = indiceDe(e);
if (k!=-1) {retireIemeElement(k);}
}

si e nest

pas

dans

private void retireIemeElement(int k) {


// prrequis
: 0<=k<nbElements
// effet
: retire le k ime lment
nbElements--;
if (nbElements==0) {elements = null;}
else if (nbElements < elements.length/facteurDeCroissance) {
// cration dun tableau plus petit
Object[] nouveau =
new Object[elements.length/facteurDeCroissance];
for (int i=0;i<k; i++) {nouveau[i]=elements[i];}
for (int i=k;i<nbElements; i++) {nouveau[i]=elements[i+1];}
elements=nouveau;
}
else {
for (int i=k;i<nbElements; i++) {elements[i]=elements[i+1];}
}
}

private void ajouteAuBout(T e) {


// effet
: ajoute llment e "au bout" du tableau des lments
if (elements!=null && nbElements<elements.length){
// place suffisante
elements[nbElements]=e;
}
// place insuffisante, cration dun nouveau tableau
else if (nbElements==0) {
elements= new Object[taillePourSingleton]; elements[0]=e;
}
else {
Object[] nouveau =
new Object[facteurDeCroissance*elements.length];
for (int i=0; i<nbElements; i++) {nouveau[i]=elements[i];}
nouveau[nbElements] = e;
elements=nouveau;
}
nbElements++;
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

288

this

Annexes

public static <T extends Comparable<T>>


Ensemble<T> union(Ensemble<T> e1, Ensemble<T> e2) {
Ensemble<T> resul = new Ensemble();
int i1=0; int i2=0;
while (i1<e1.nbElements && i2<e2.nbElements) {
T v1 = (T) e1.elements[i1]; T v2 = (T) e2.elements[i2];
int compar=v1.compareTo(v2);
if (compar<0) {resul.ajouteAuBout(v1); i1++;}
else if (compar>0) {resul.ajouteAuBout(v2); i2++;}
else {resul.ajouteAuBout(v1); i1++; i2++;}
}
while (i1<e1.nbElements) {
resul.ajouteAuBout((T) e1.elements[i1]); i1++;
}
while (i2<e2.nbElements) {
resul.ajouteAuBout((T) e2.elements[i2]); i2++;
}
return resul;
}
public static <T extends Comparable<T>>
Ensemble<T>
intersection(Ensemble<T>
e1, Ensemble<T>
Ensemble<T> resul = new Ensemble();
int i1=0; int i2=0;
while (i1<e1.nbElements && i2<e2.nbElements) {
T v1 = (T) e1.elements[i1]; T v2 = (T) e2.elements[i2];
int compar=v1.compareTo(v2);
if (compar<0) {i1++;}
else if (compar>0) {i2++;}
else {resul.ajouteAuBout(v1); i1++; i2++;}
}
return resul;
}
public static <T extends Comparable<T>>
Ensemble<T> difference(Ensemble<T> e1, Ensemble<T> e2) {
Ensemble<T> resul = new Ensemble();
int i1=0; int i2=0;
while (i1<e1.nbElements && i2<e2.nbElements) {
T v1 = (T) e1.elements[i1]; T v2 = (T) e2.elements[i2];
int compar=v1.compareTo(v2);
if (compar<0) {resul.ajouteAuBout(v1); i1++;}
else if (compar>0) {i2++;}
else {i1++; i2++;}
}
while (i1<e1.nbElements) {
resul.ajouteAuBout((T) e1.elements[i1]); i1++;
}
return resul;
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

289

e2)

Annexes

//=================== parcoureur densemble ===================


public class Parcours implements parcours.Parcours<T>{
private int courant;
Parcours() {
// Parcours initialis sur le plus petir lment
courant=0;
}
Parcours(Parcours p) {
// Parcours initialis avec ltat du Parcours p
courant=p.courant;
}
public void tete() {
// effet: positionne
this sur le plus petit lment
// (parcours en fin si ensemble vide)
courant=0;
}
public void suivant() {
// effet
: positionne this sur llment suivant,
// ne fait rien si estEnFin()
if (courant<nbElements) {courant++;}
}
public boolean estEnFin() {
// rsultat
: indique si this est en fin
// (au dela de la queue, en dea de la tte)
return courant==nbElements;
}
public void retireElement() {
// prrequis
: this nest pas en fin
// effet
: retire llment courant de this
// le suivant de llment retir, sil existe, devient
// llement courant, sinon this passe en fin de parcours
retireIemeElement(courant);
}
public T elementCourant() {
// prrequis
: this nest pas en fin
// rsultat
: llment courant de this
if (estEnFin()) {
System.out.println(
"erreur
: acces hors domaine de lensemble");
Thread.dumpStack(); System.exit(0);
}
return (T) elements[courant];
}
}
//=============================================================
}

Module A.P.I. Algorithmique Programmation Imprative

Universit de Rennes 1

290

de lense

Vous aimerez peut-être aussi