Académique Documents
Professionnel Documents
Culture Documents
VISUAL BASIC
RAKOTOARISOA Tahiana
Décembre 2006
Je dédie ce livre à
la mémoire de mon arrière–grand–père
Nicolas Chantereau
dit « le bourru »
2
INTRODUCTION
Ce manuel est un résumé de la syntaxe du langage Visual Basic. Nous utilisons le VB 6.0 de
Microsoft sur Windows XP Professionnel. Dans la présentation de la syntaxe de ce langage
(son grammaire), nous utilisons les conventions suivantes
– les textes qui doivent être écrits tels quels (terminaux) sont en courier new gras
– les noms qui doivent être remplacés par la définition correspondante sont en courier
new normal
– la barre verticale | dénote le choix
– la virgule , dénote la concaténation
– les éléments entre crochets [ et ] sont obligatoires
– les éléments entre parenthèses ( et ) sont facultatifs
– les éléments précédés d’un étoile * peuvent être répétés 0, 1 ou plusieurs fois
Le corps de texte est en Times New Roman normal. Les nouveaux termes et les mises en
garde sont en Times New Roman gras. Les programmes et les noms de variables sont en
Courier New normal noir. Les commentaires dans les programmes sont en Courier New
normal gris. Les textes affichés par l’ordinateur sont en Courier New gras noir. Dans la
présentation des programmes et fonctions prédéfinis, nous n’indiquons pas les noms des
paramètres pour être plus concis. Les opérateurs sont aussi placés avant ou entre les
paramètres.
sub ( bool = bool )
3
1 ère PARTIE. STRUCTURE D’UN ORDINATEUR
Numération
Le problème de la numération est celui de l’écriture de tous les nombres avec un ensemble
fini de symboles appelés chiffres 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C,… Le nombre x noté
unun-1...u1u0,u-1u-2... qui est une suite de chiffres représente un polynôme en a, a étant
la base de numération
Le nombre 453,7 est écrit en base en 10. Pour l’écrire en base 7, calculons le nombre x tel
que 7x ≤ 453,7 < 7x+1 : x est la partie entière du logarithme base 7 de 453,7
Divisons alors 453,7 par 73, puis le reste de cette division par 72, etc.
453,71010 = 1215,46277
Booléen
Une variable binaire (ou booléenne, de George Bool, logicien et mathématicien britannique)
est une variable qui peut prendre 2 valeurs seulement, notées { 0 ; 1 } ou { faux ;
vrai } ou { non ; oui }. Il existe 4 fonctions booléennes avec un seul paramètre booléen
c’est–à–dire de type
4
f : B → B où B = { 0 ; 1 }
x → y
x 0 1
f0(x) 0 0 constante 0
f1(x) 0 1 fonction identité
f2(x) 1 0 ¬ x, non x
f3(x) 1 1 constante 1
f : A → B
f : B × B → B
(x1,x2)→ y
x1 0 0 1 1 x1 0 0 1 1
x2 0 1 0 1 x2 0 1 0 1
f0(x1,x2) 0 0 0 0 constante 0 fF(x1,x2) 1 1 1 1 constante 1
f1(x1,x2) 0 0 0 1 x1 et x2, x1*x2 fE(x1,x2) 1 1 1 0 ¬(x1 et x2)
f2(x1,x2) 0 0 1 0 x1 > x2 (0 = non) fD(x1,x2) 1 1 0 1 x1 ≤ x2, x1 x2
f3(x1,x2) 0 0 1 1 x1 fC(x1,x2) 1 1 0 0 non x1
f4(x1,x2) 0 1 0 0 x1 < x2 fB(x1,x2) 1 0 1 1 x1 ≥ x2
f5(x1,x2) 0 1 0 1 x2 fA(x1,x2) 1 0 1 0 non x2
f6(x1,x2) 0 1 1 0 x1 ≠ x2 f9(x1,x2) 1 0 0 1 x1 = x2, x1 x2
f7(x1,x2) 0 1 1 1 x1 ou x2, x1+x2 f8(x1,x2) 1 0 0 0 ¬(x1 ou x2)
Un nom sert à distinguer une variable. Un nom est formé d’une lettre suivi éventuellement
par des lettres, des chiffres ou des _ et ne doivent pas contenir des mots–clés (255 caractères
au maximum)
cNom = lettre,*(lettre|_|chiffre)
lettre := a | b | c | d | e | f | g | h | i | j |
k | l | m | n | o | p | q | r | s | t |
u | v | w | x | y | z
A | B | C | D | E | F | G | H | I | J |
K | L | M | N | O | P | Q | R | S | T |
U | V | W | X | Y | Z
5
chiffre := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Pour créer une variable, il suffit d’écrire dim, son nom, as et son type
dim b9 as boolean
Le programme
sub ( boolean = boolean )
b9 = 0
Lorsque d'autres types de données numériques sont convertis en valeurs de type boolean, 0
devient false et toutes les autres valeurs deviennent true. Lorsque des valeurs de type
boolean sont converties en d'autres types de données, false devient 0 et true devient -1.
Les fonction suivantes sont définies sur les booléens
Les fonctions
convertissent un booléen en texte (Faux ou Vrai si Windows est en français) et vice versa.
cBool accepte faux, vrai, false et true et même des littéraux nombres.
Entier naturel
D’autre part, puisque les circuits de calculs sont « gravés » une fois pour toutes sur
les « puces », il faut également utiliser un nombre fini de chiffres binaires ou bit (de l’anglais
« binary digit »). Avec 3 bits, on peut manipuler les nombres allant de 0 à 7
0 0 0 0
6
0 0 1 1
0 1 0 2
0 1 1 3
1 0 0 4
1 0 1 5
1 1 0 6
1 1 1 7
Avec n bits, on peut manipuler des nombres allant de 0 à 2n-1. VB possède le type
Les textes après ' ou rem jusqu’à la fin de ligne sont des commentaires et ils sont ignorés par
le compilateur. Exemple : pour créer un entier naturel sur 8 bits, nous écrivons :
dim n as byte
Un littéral entier naturelen base 10 est formé par une suite de chiffres 0 à 9. Un littéral entier
relatif en base 8 commence par un &O ou &o suivi par une suite de chiffres 0 à 7. Un littéral
entier relatif en base 16 commence par un &H ou &h suivi par une suite de chiffres 0 à 9 ou de
lettres A à F ou a à f.
cNat = cDec,*cDec
| &o,cOct,*cOct
| &h,cHex,*cHex
cDec = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
cOct = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
cHex = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
a | b | c | d | e | f | A | B | C | D | E | F
Les programmes
copient la valeur d’un entier relatif à un autre entier relatif. Les instructions suivantes sont
équivalentes
n = 123
n = &o173
n = &h7B
'comparaison :
function ( byte = byte ) as boolean 'égal
function ( byte <> byte ) as boolean 'différent
function ( byte < byte ) as boolean 'inférieur
function ( byte > byte ) as boolean 'inférieur ou égal
function ( byte <= byte ) as boolean 'inférieur ou égal
function ( byte >= byte ) as boolean 'supérieur ou égal
'arithmétique :
function ( byte + byte ) as byte 'addition
function ( byte - byte ) as byte 'soustraction
function ( byte * byte ) as byte 'multiplication
7
function ( byte / byte ) as byte 'division
function ( byte \ byte ) as byte 'quotient entier
function ( byte mod byte ) as byte 'reste de la division entière
function ( byte ^ byte ) as byte 'puissance
Nous utilisons une étoile * pour la multiplication pour ne pas confondre le croix × avec la
lettre x. Les comparaisons sont évaluées après les fonctions arithmétiques. Les additions et
soustractions sont évaluées après les multiplications et divisions, qui sont évaluées après la
puissance. Les instructions suivantes sont équivalentes
n = 0 = 1 + 2 * 3 ^ 4
n = ( 0 = ( 1 + ( 2 * ( 3 ^ 4 ) ) ) )
Les fonctions
convertissent un octet en son équivalent texte et vice–versa. oct renvoie un long entre
200 0000 0000 et 177 7777 7777 sans le préfixe &o, hex renvoie un long entre 8000 0000
et 7FFF FFFF sans le préfixe &h. cByte accepte des littéraux réels dans le texte écrits avec la
syntaxe de Windows. Si vous avez spécifié dans Panneau de configuration\Options
régionales et lingistique puis sur le bouton Personnaliser que le symbole décimal
est la virgule et que symbole de groupement des chiffres est l’espace, alors il faut utiliser une
virgule (au lieu d’un point) et on peut entrer des espaces n’importe où dans la mantisse. Mais
si vous entrer des littéraux octaux ou héxadécimaux, alors il faut suivre la syntaxe de VB (pas
d’espace).
Entier relatif
Pour représenter des nombres négatifs, nous ajoutons un bit supplémentaire pour le signe
0 1 1 0 1 0 0 0
valeur absolue
signe
Si les circuits d’addition pour les entiers naturels sont également utilisés pour les entiers
négatifs, le résultat est faux
1 1
0 1 1 0 6
+ 1 0 1 0 + (-2)
= 1 0 0 0 0 0 ?
? 0 ?
Une solution consiste à calculer la négation de chaque bit, y compris le signe, pour
représenter l’opposé d’un nombre donné
8
1
2 0 0 1 0 0 1 1 0 6
(-2) 1 1 0 1 + 1 1 0 1 + (-2)
3 ?
= 1 0 0 1 1
+ 3 ? ?
Il faudrait ajouter la retenue 1 au résultat. Plus facilement, nous ajoutons ce 1 lors du calcul
de l’opposé plutôt que lors de l’addition et la retenue est alors ignorée
1 1
2 0 0 1 0 0 1 1 0 6
(-2) 1 1 1 0 + 1 1 1 0 + (-2)
= 1 0 1 1 0 4
+ 4 ?
dim n as integer
Un littéral entier relatif en base 10 est formé par un signe + ou – (éventuellement), une suite
de chiffres 0 à 9. Un littéral entier relatif en base 8 commence par un &O ou &o suivi par une
suite de chiffres 0 à 7 (il n’y a pas de signe!). Un littéral entier relatif en base 16 commence
par un &H ou &h suivi par une suite de chiffres 0 à 9 ou de lettres A à F ou a à f (il n’y a pas
de signe!).
cEnt = (+|-),cDec,*cDec
| &o,cOct,*cOct
| &h,cHex,*cHex
copient la valeur d’un entier relatif à un autre entier relatif. Les instructions suivantes sont
équivalentes
n = -123456
n = &o37777416700
n = &hFFFE1DC0
9
sub ( long = integer )
sub ( integer = long ) 'etc
'comparaison :
function ( integer = integer ) as boolean 'égal
function ( integer <> integer ) as boolean 'différent
function ( integer < integer ) as boolean 'inférieur
function ( integer > integer ) as boolean 'inférieur ou égal
function ( integer <= integer ) as boolean 'inférieur ou égal
function ( integer >= integer ) as boolean 'supérieur ou égal
'arithmétique :
function ( integer + integer ) as integer 'addition
function ( integer - integer ) as integer 'soustraction
function ( integer * integer ) as integer 'multiplication
function ( integer / integer ) as integer 'division
function ( integer \ integer ) as integer 'quotient entier
function ( integer mod integer ) as integer 'reste
function ( integer ^ integer ) as integer 'puissance
'comparaison :
function ( long = long ) as boolean 'égal
function ( long <> long ) as boolean 'différent
function ( long < long ) as boolean 'inférieur
function ( long > long ) as boolean 'inférieur ou égal
function ( long <= long ) as boolean 'inférieur ou égal
function ( long >= long ) as boolean 'supérieur ou égal
'arithmétique :
function ( long + long ) as long 'addition
function ( long - long ) as long 'soustraction
function ( long * long ) as long 'multiplication
function ( long / long ) as long 'division
function ( long \ long ) as long 'quotient entier
function ( long mod long ) as long 'reste de la division entière
function ( long ^ long ) as long 'puissance
On peut additionner, soustraire, etc. des entiers de précisions différentes, celui de plus faible
précision sera converti à la plus grande précision. Les fonctions
convertissent un entier relatif en son équivalent texte et vice–versa. oct renvoie jusqu’à 11
caractères octaux sans le préfixe &o, hex renvoie jusqu’à 8 caractères héxadécimaux sans le
préfixe &h. cInt et cLng acceptent des littéraux réels dans le texte et même des espaces.
10
Réel fixe
Si les circuits d’addition pour les entiers sont également utilisés pour les réels, alors il suffit
de fixer, par pure convention, la position de la virgule. On obtient ainsi des nombres en
virgule fixe
0 1 1 0 1 0 0 1 1 0 1 0 0 0 0 0
;
signe partie entière partie fractionnaire
qui utilise 64 bits. En fait, un currency est mémorisé dans un entier 64 bits, et il suffit de le
multiplier par 10 000 lors de la conversion en entier et le diviser par 10 000 lors de la
conversion en texte. Exemple : pour créer un réel fixe sur 64 bits, nous écrivons :
dim c as currency
Un littéral réel fixe est formé par un signe + ou – (éventuellement), une suite de chiffres 0 à
9, un point (éventuellement), une suite de chiffres 0 à 9 (éventuellement). Si les chiffres
avant ou après le point sont nuls, ils peuvent être omis
cFixe = (+|-),cDec,*cDec,.,cDec,*cDec
| (+|-),cDec,*cDec,.
| (+|-),.,cDec,*cDec
VB, qui est a été inventé par des américains, utilise des points là où les français utilisent des
virgules et vice–versa ! De plus, les américains n’aiment pas mettre un zéro avant le point. Le
programme
c = .123
'comparaison :
function ( currency = currency ) as boolean 'égal
function ( currency <> currency ) as boolean 'différent
function ( currency < currency ) as boolean 'inférieur
function ( currency > currency ) as boolean 'inférieur ou égal
function ( currency <= currency ) as boolean 'inférieur ou égal
11
function ( currency >= currency ) as boolean 'supérieur ou égal
'arithmétique :
function ( currency + currency ) as currency 'addition
function ( currency - currency ) as currency 'soustraction
function ( currency * currency ) as currency 'multiplication
function ( currency / currency ) as currency 'division
function ( currency \ currency ) as currency 'quotient entier
function ( currency mod currency ) as currency 'reste
function ( currency ^ currency ) as currency 'puissance
\ et mod convertissent d’abord les nombres en entiers avant de calculer le quotient ou le reste.
Réel flottant
Un réel fixe ne permet pas de manipuler les très grands nombres, car il faut toujours utiliser
un nombre fini de bits. La solution consiste à ne conserver que les chiffres les plus
significatifs
Ces chiffres les plus significatifs s’appellent la mantisse, et la puissance de 2 (qu’il faut aussi
conserver) s’appelle l’exposant. On obtient ainsi des nombres à virgule flottante
0 1 1 0 1 0 0 1 0 1 1 1 0
;
mantisse exposant
Un littéral réel flottant est formé par un signe + ou – (éventuellement), une suite de chiffres 0
à 9, un point (éventuellement), une suite de chiffres 0 à 9 (éventuellement), une lettre e ou E
(éventuellement), un signe + ou – (éventuellement), une suite de chiffres 0 à 9
cReel = (+|-),*cDec,(.),*cDec,(e|E,(+|-),*cDec)
Si le signe de la mantisse ou de l’exposant est +, il peut être omis. Si les chiffres avant ou
après le point sont tous nuls, ces chiffres peuvent être omis. Les programmes
copient la valeur d’un réel flottant à un autre réel flottant. Les instructions suivantes sont
équivalentes
n = +123456E-6
12
n = +123456.e-6
n = +.123456
n = .123456
n = 0.123456
n = 0.0123456e+1
n = 0.0123456e1
'comparaison :
function ( single = single ) as boolean 'égal
function ( single <> single ) as boolean 'différent
function ( single < single ) as boolean 'inférieur
function ( single > single ) as boolean 'inférieur ou égal
function ( single <= single ) as boolean 'inférieur ou égal
function ( single >= single ) as boolean 'supérieur ou égal
'arithmétique :
function ( single + single ) as single 'addition
function ( single - single ) as single 'soustraction
function ( single * single ) as single 'multiplication
function ( single / single ) as single 'division
function ( single \ single ) as single 'quotient entier
function ( single mod single ) as single 'reste
function ( single ^ single ) as single 'puissance
'comparaison :
function ( double = double ) as boolean 'égal
function ( double <> double ) as boolean 'différent
function ( double < double ) as boolean 'inférieur
function ( double > double ) as boolean 'inférieur ou égal
function ( double <= double ) as boolean 'inférieur ou égal
function ( double >= double ) as boolean 'supérieur ou égal
'arithmétique :
function ( double + double ) as double 'addition
function ( double - double ) as double 'soustraction
function ( double * double ) as double 'multiplication
function ( double / double ) as double 'division
function ( double \ double ) as double 'quotient entier
function ( double mod double ) as double 'reste
function ( double ^ double ) as double 'puissance
function sqr ( double ) as double ' racine carrée
function exp ( double ) as double ' exponentielle
function log ( double ) as double ' logarithme népérien
function sin ( double ) as double ' angle en radian
function cos ( double ) as double ' angle en radian
function tan ( double ) as double ' angle en radian
function atn ( double ) as double ' résultat entre ]-/2../2[ radian
13
On peut additionner, soustraire, etc. des réels de précisions différentes, celui de plus faible
précision sera converti à la plus grande précision.
Les fonctions
Caractère
Pour entrer un texte dans un ordinateur, il faudrait d’abord le remplacer par des 0 et des 1. Un
caractère est une lettre, un chiffre ou un symbole ( +, -, *, /, ., ; , ; , ?, etc.). Il faut au
minimum 6 ou 7 bits pour représenter un caractère puisque 26 (lettres) + 10 (chiffres) = 36 et
26 = 64. Ainsi l’ASCII ( ou « American Standard Code for Information Interchange ») utilise
8 bits dont les 128 premiers caractères sont
0 1 2 3 4 5 6 7 8 9 A B C D E F
1 DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US
2 SP ! " # $ % & ’ ( ) * + , - . /
3 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
4 @ A B C D E F G H I J K L M N O
5 P Q R S T U V W X Y Z [ \ ] ^ °
6 ` a b c d e f g h i j k l m n o
7 p q r s t u v w x y z { | } _ DEL
14
NUL Null DLE Data link escape
SOH Start of heading (début d’en–tête) DC1 Direct control 1
STX Start of text NAK Negative acknowledge
ETX End of text SYN Synchronous idle
EOT End of transmission ETB End of transmission block
ENQ Enquiry (demande) CAN Cancel
ACQ Acknowledge (accusé de récep°) EM End of medium (support)
BEL Bell (sonnerie) SUB Substitute
BS Backspace (retour arrière) ESC Escape
HT Horizontal tabulation FS File separator (sépar. de fichier)
LF Line feed (nouvelle ligne) GS Group separator
VT Vertical tabulation RS Record separator
FF Form feed (nouvelle page) US Unit separator (sous–article)
CR Carriage return (retour chariot) SP Space (espace)
SO Shift Out (hors code) DEL Delete (suppression)
SI Shift in (en code)
Exemple : le code du J est 4A en base 16 et 74 en base 10. Il n’y a pas de type caractère en
VB.
Opérateur et/ou
Il existe un moyen simple pour transformer n’importe quelle fonction binaire en n’utilisant
que les opérateurs et, ou et non. Considérons d’abord la table donnant les résultats de la
fonction (sa « table de vérité »)
0 1 2 3 4 5 6 7
a 0 0 0 0 1 1 1 1
b 0 0 1 1 0 0 1 1
c 0 1 0 1 0 1 0 1
f(a,b,c) 1 0 1 0 0 0 1 0
Prenons alors les colonnes pour lesquelles f(a;b;c) est égal à 1 (si les 1 sont plus nombreux
que les 0, il suffit de prendre les colonnes dont f(a;b;c) est égal à 0 et inverser ensuite la
formule finale). La colonne 2 signifie par exemple que lorsque
si a = 0 et b = 1 et c = 0 alors f(a,b,c)=1
pour que a*b*c = 1 il faut que ces 3 variables soient tous égal à 1. Il suffit alors de prendre
la négation des variables qui sont égales à 0 et la colonne 2 est égale à
(¬a)*b*(¬c) = (¬0)*1*(¬0) = 1
Nous faisons de même pour les colonnes 0 et 6, et la formule recherchée est la « somme » de
ces produits
15
Ordinateur câblé
Dans un ordinateur numérique câblé, chaque variable est véhiculée par un fil électrique et ne
peut prendre que 2 valeurs, par exemple 5 V et 0 V (présence et absence de courant). Pour
réaliser l’opérateur non, supposons que la variable peut actionner un électro–aimant : si un
courant passe, l’électroaimant peut repousser un interrupteur
a a
a
non a
non a
a = 1 ; non a = 0 a = 1 ; non a = 0
Nous simplifions ce schéma par
Pour réaliser l’opérateur et, il suffit de mettre en série les deux variables
a b
a et b
a ou b
b
a b c
a b c
a b c
En reliant ces 3 opérateurs, nous pouvons obtenir des fonctions plus complexes. Mais si nous
voulons faire d’autres calculs, il faut refaire tout le câblage.
16
Ordinateur programmé
¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬
et et et et
données
+ * - /
résultat
0000 0001 0010 0011
Dans la pratique, le code d’instruction est constitué de 8 ou 16 bits. Les opérations à exécuter
et les résultats intermédiaires sont conservés dans une « mémoire » qui est constitué par des
bits placés côte à côte. Chaque élément possède un numéro (une adresse) pour l’identifier
bus d’
adresse
¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬ ¬
et et et et
bus de
données
processeur 1 0 0 1
Le processeur désigne l’ensemble des circuits de calculs. Dans la pratique, une adresse est
constituée de 16, 24 ou 32 bits et le bus de données est formé de 8, 16, 32 ou 64 bits : le
processeur peut donc lire ou écrire 8, 16, 32 ou 64 bits à la fois. Une instruction complète est
formée par un code (8 bits minimum) et, éventuellement, une donnée (64 bits maximum). Les
instructions plus larges que le bus de données sont lues en plusieurs étapes.
Certains processeurs possèdent une mémoire spéciale appelée registre accumulateur dans
lequel sont conservés un opérande et le résultat. Ce type de processeur peut exécuter les
instructions suivantes :
– charger x : mettre la donnée x dans l’accumulateur
– charger_adr a : mettre la donnée située à l’adresse a dans l’accumulateur
17
– add x : calculer la somme de x et de l’accumulateur et mettre le résultat dans
l’accumulateur
– add_adr a : additionner la donnée d’adresse a au contenu de l’accumulateur
– ranger a : copier le contenu de l’accumulateur dans la mémoire d’adresse a
En fait, il faut aussi indiquer si l’on doit lire un octet ( 8 bits) ou un entier(16, 32 ou 64 bits)
ou un réel (32 ou 64 bits), etc. Il existe alors plusieurs instructions charger :
– charger_octet x
– charger_ent16 x
– charger_ent32 x
– charger_octet_adr a
– charger_ent16_adr a
Comme un ordinateur ne sait manipuler que des 0 et des 1, une instruction doit être formée
par des 0 et des 1 également. Mais au lieu de manipuler réellement des 0 et des 1, nous
utilisons plutôt une représentation en base 16. Par exemple charger_ent16_adr 275 devient
A1 0000 0113 : cette instruction est composée de 5 octets (1 pour le code et 4 pour l’adresse)
A1 00 00 01 13
code paramètre
Pour faciliter encore l’entrée d’une instruction, nous utilisons des mots plus simples à retenir
(« mnémoniques ») et des nombres en base 10 : un programme appelé « assembleur » se
charge alors de les transformer en une suite de 0 et de 1.
Un programme est formé par des instructions placées côte à côte dans la mémoire. Le
processeur possède un registre appelé pointeur d’instruction qui contient l’adresse de
l’instruction qu’il doit exécuter. Au démarrage de l’ordinateur, ce pointeur est égal à 0 : le
processeur commence toujours par exécuter l’instruction d’adresse 0. Après avoir exécuté
une instruction, le processeur augmente ce pointeur selon la longueur de l’instruction qu’il
vient d’exécuter. Par exemple, pour calculer la somme des entiers 16 bits situés à l’adresse
966 et 4205 et mettre le résultat à l’adresse 747
0 lire_ent16_adr ( 966 ) ;
5 add_ent16_adr ( 4205 ) ;
10 écrire_ent16 ;
Les nombres à gauche indiquent l’adresse de chaque instruction. Le processeur possède aussi
un registre de 8 ou 16 bits permettant de garder le résultat des comparaisons (pour conserver
l’accumulateur en vue d’une autre opération) ou de signaler un dépassement de capacité, etc.
– comp x : comparer x et l’accumulateur et mettre le résultat dans un indicateur
– saut_inf a : sauter à l’instruction d’adresse a si l’indicateur est égal à « inférieur »
sinon exécuter l’instruction après ce saut_inf (saut conditionnel)
– aller_en a : sauter à l’instruction d’adresse a (saut inconditionnel, « GOTO »)
Pour calculer la valeur absolue de l’entier à l’adresse 966 et mettre le résultat à l’adresse 747
0 lire_ent16_adr ( 966 ) ;
5 comp_ent16 ( 0 ) ;
10 saut_sup ( 16 ) ;
15 opposé ;
16 écrire_ent16 ( 747 ) ;
18
Comme nous pouvons le constater, l’indication des adresses des données ou des instructions
est très fastidieuse. Un « gestionnaire de mémoire » doit trouver un espace libre pour mettre
les instructions et les données. Les adresses sont remplacées par des mots : notre but est de
pouvoir écrire des formules telles qu’on utilise habituellement en mathématique en se servant
du clavier d’une machine à écrire. Le « traducteur » se charge alors de décomposer ces
instructions de « haut niveau ». Il existe deux types de traducteurs :
– un compilateur traduit le programme « source » en entier avant son exécution
– un interpréteur traduit chaque ligne que l’utilisateur a introduit et l’exécute
immédiatement sans mémoriser la forme exécutable
Un interpréteur est plus rapide si l’on apporte des modifications au programme mais un
compilateur est plus efficace s’il a des instructions répétées fréquemment. Le programme
source est un simple fichier texte avec une extension .cpp pour C++. Un programme peut
utiliser des programmes mémorisés dans d’autres fichiers, en général les fichiers d’en–tête
(header file, extension .h). Les programmes sources sont donc transformés par le compilateur
en une forme intermédiaire, appelé fichier objet (extension .o) où les adresses ne sont pas
encore fixées. Puis, un « éditeur de lien » et un « chargeur » assemble tous les fichiers objet
et calcule l’adresse de chaque instruction pour produire le fichier exécutable (extension .exe
sous Windows, sans extension sous Unix).
Source : En–tête :
xy_dist.cpp xy.h
Compilateur
Objet : Objet :
xy_dist.o xy.o
Éditeur de
liens
Exécutable :
xy_dist.exe
Structures de contrôle
Comme un processeur est constitué d’un nombre fini de circuits, il faut des moyens
permettant de combiner ses fonctions (ou instructions) élémentaires :
– L’enchaînement : nous séparons une instruction avec celle qui doit être exécutée ensuite par
un retour chariot
19
ou par un deux–point, si les instructions tiennent sur une seule ligne
dim _
x as double
Les décalages à droite (indentation) permettent d’éviter les erreurs. S’il n’y rien à exécuter
dans la branche else, alors on peut la supprimer
y = x
if x <= 0 then ' x ≤ 0
y = (x)
end if
y = x
if x <= 0 then y = (-x)
On peut aussi utiliser plusieurs conditions. Exemple : nombre de solutions d’une équation
réelle du 2nd degré
d = b*b - 4*a*c
if d < 0 then
nb = 0
elseIf d = 0 then
nb = 1
x1 = (-b)/(2*a)
else ' d > 0
nb = 2
x1 = (-b-sqr(d))/(2*a)
x2 = (-b+sqr(d))/(2*a)
end if
20
Après les initialisations, i est comparé à b. Si i est supérieur à n alors, on sort de la
« boucle », c’est–à–dire que l’on saute à l’instruction après le loop (ici, il n’y a rien et le
calcul s’arrête). Sinon, y est multiplié par a, puis i est incrémenté de 1 et on revient au début
de la boucle, c’est–à–dire au test i > b. On peut mettre plusieurs instructions exit do dans
une boucle. La condition de sortie étant la première instruction, on peut écrire
y = 1 : i = 1 ' y = ab
while i > b
y = y*a : i = i+1
wend
y = 1
for i = 1 to b step 1 ' i [1,b] faire ...
y = y*a
next
y = 1
for i = 1 to b ' i [1,b] faire ...
y = y*a
next
Fonction
tangente : R → R
x → sin x / cos x
s’écrit
Nous avons groupé la déclaration et l’initialisation de la fonction. Pour les fonctions qui ne
peuvent pas être définies par une seule formule
integer f(integer x ) {
integer a
a = 1
integer b ; ' etc.
21
}
provoquera une erreur. Les variables locales sont détruites à la fin de la fonction. Une
fonction peut faire appel à elle–même (fonction récursive). Exemple : calcul de la factorielle
Après avoir défini une fonction, nous pouvons l’utiliser dans une formule (ou expression)
t = 1 + tan(3.14)*2
Concrètement, une fonction est une liste d’instructions en langage machine, c’est–à–dire un
pointeur. En utilisant des aller_en et des sauts conditionnels, ces instructions ne sont pas
forcément mises côte à côte dans la mémoire.
Programme
Si un paramètre est à la fois donnée et résultat, s’il n’y a que des résultats, ou s’il n’y a aucun
paramètre, nous utilisons alors des programmes. Pour indiquer qu’un paramètre est une
donnée, il faut précéder son nom par byVal
Après avoir défini un programme, nous pouvons l’utiliser (l’appeler) dans une instruction.
dim w as double
w = 6
add ( w, 1 )
Adresse
Une adresse (ou une référence ou un pointeur) est un numéro permettant de distinguer chaque
élément (bit ou octet) de la mémoire. Si la mémoire est composée de n éléments, alors une
adresse doit être composée au minimum de log2 n bits. Dans la pratique, on utilise un
multiple de 8 tel que 16, 24, 32 ou 64 bits. En C++, la fonction new cherche un endroit libre
capable de contenir le type de données spécifié dans la mémoire centrale. La fonction delete
libère la mémoire pointée par une adresse. La fonction & retourne l’adresse d’une variable
22
double *a, *b ; a
a = new double ; a
a 1.5
*a = 1.5 ;
a 1.5 x 1.5
x = *a ;
delete [] a ; a x 1.5
b
x
a = &x ; a 1.5
b « pointe » maintenant sur une partie de la mémoire qui est déjà libérée. On dit que c’est une
référence folle et une telle situation doit être évitée. Il n’y a pas de type adresse en VB.
Article
Les multiplets (au sens mathématique du terme) sont représentés par des articles (ou des
enregistrements). Les parties (ou champs) sont placées côte à côte dans la mémoire. Il n’y a
pas de littéral article. Il faut accéder aux champs un par un. On ne peut pas tester l’égalité de
deux articles (x1 = x2). De même, l’affichage d’un article doit se faire champ par champ
Répertoire
Un répertoire est un article spécial permettant de grouper sur des « unités » (disquette, disque
dur, CD, DVD, flash disk, etc.), les objets (fichiers et répertoires) créés par l’utilisateur. À
l’installation de Windows, il détecte automatiquement les unités et partitions (un disque peut
être divisé en plusieurs partitions) existantes sur l’ordinateur et leur attribue une lettre pour
les identifier (a: pour le premier lecteur de disquette, b: pour le deuxième, c: pour le
23
premier disque dur, d: pour le lecteur CD, etc. : le nom d’une unité est toujours suivi d’un
deux–points). Il crée aussi un répertoire nommé Documents and Settings dans le disque c:
(un nom en Windows peut contenir un espace ). Puis, il crée un répertoire pour chaque
utilisateur (p. ex Tah) dans ce c:\Documents and Settings. (Windows utilise un
backslash \ pour accéder aux « champ » d’un répertoire et non pas un point comme C++). Au
démarrage de l’ordinateur, l’unité courante est celle qui contient Windows (p. ex c:). Quant
l’utilisateur Tah entre une session sur l’ordinateur, le répertoire courant est
Administrateur
Documents and Settings
Tah
...
a: BC5
c: Program Files Office
Microsoft Office
Template
...
addins
Windows
AppPatch
...
tel qu’il est écrit dans la fenêtre de l’Invite de commande (Démarrer\Tous les
programmes\Accessoires\Invite de commande). Pour changer le répertoire courant, nous
utilisons la commande cd ou chdir (change directory)
cd c:\Program Files
C:\Program Files>_
(La plupart des programmes sont installés dans le répertoire c:\Program Files). Windows
n’est pas sensible aux minuscules et majuscules. On aurait put écrire
cd C:\pRogram files
cd \
C:\>_
md Tah
Administrateur
Documents and Settings
Tah
...
a: Program Files BC5
Office
c: Microsoft Office
Template
Tah ...
addins
Windows
AppPatch
...
Pour supprimer un répertoire, nous utilisons la commande rd ou rmdir (remove directory)
24
rd Tah
Pour afficher le contenu d’un répertoire, nous utilisons la commande dir (directory)
dir \
Répertoire de C:\
dir sans aucun paramètre affiche le contenu du répertoire courant. La notion de répertoire
courant est très importante. Tout fichier ou répertoire en dehors du répertoire courant est
invisible pour Windows sauf si vous précisez son nom complet. Exemple :
provoquera
Fichier introuvable
Pour les programmes (fichier exécutables .exe, .com, etc.), nous pouvons encore indiquer
dans quel répertoire, Windows doit les chercher s’ils ne se trouvent pas dans le répertoire
courant via la commande path
ajoute le répertoire c:\tah aux répertoire de recherches existants. path sans paramètres
affiche les répertoires de recherches actuels. Mais lorsque vous fermez l’invite de
commandes, les répertoires de recherche reviennent à leurs états précédents. Il vaut mieux
utiliser Démarrer\Panneau de configuration\Système\Avancé\Variables d’environn
ement, sélectionner Path, puis ajouter le répertoire dans Valeur de la variable. Les deux
Path de Variables utilisateur et Variables système forment le Path de l’invite de
commande.
Tapez help pour obtenir la liste de toutes les commandes Windows disponibles. Tapez help
suivi du nom de la commande pour obtenir plus de détails sur l’utilisation d’une commande
donnée. Exemple :
help prompt
25
Programme principal
Un programme « source » en C++ est un fichier texte ASCII avec l’extension .cpp. Ce
fichier doit contenir un programme spécial appelé main. Si ce programme utilise des
programmes, des types, des constantes, etc. contenus dans d’autres fichiers appelés fichiers
d’en–tête (header files), le nom de ces fichiers devront être indiqués dans le fichier .cpp.
Exemple : pour calculer 1+2
Nous supposons que ce fichier s’appele c_12.cpp et se trouve dans le répertoire c:\tah\c\
debug\source\cours. Le programme printf se trouve dans le fichier d’en–tête stdio.h
qui se trouve dans le répertoire d:\bc5\include.Ce fichier source produira un fichier objet
c_12.obj que nous allons mettre dans c:\tah\c\debug\object et un fichier exécutable
c_12.exe, à mettre dans c:\tah\c\debug\exe. Il faut alors indiquer au compilateur tous
ces répertoires via le menu Option\Project\Directories
Source Directories :
Include: d:\bc5\include ; c:\tah\c\lib\source
Library: d:\bc5\lib
Source:
Output Directories :
Intermediate: c:\tah\c\debug\object
Final: c:\tah\c\debug\exe
(Nous allons mettre les types et les programmes qui les manipulent dans c:\tah\c\lib\
source). Ensuite nous lançons la compilation par le menu Project\Build all ou par le
bouton Build project. Si vous lancez le fichier exécutable via le bouton Run, vous verrez
une fenêtre qui s’affiche pendant un dixième de secondes ! Il faut alors ouvrir une fenêtre
Invite de commande puis taper le nom du fichier exécutable
Classe
Une classe est un article spécial : une classe peut avoir comme paramètre une autre classe ou
un objet quelconque (entier, etc.). Le type de ces paramètres sont listés après le mot
template avant la liste des champs. Par exemple, pour créer le type point xy en 2 dimensions
en coordonnées cartésiennes (dont les cordonnées peuvent être des entiers ou des réels)
26
Il faut déclarer que les champs x et y sont public, sinon ils ne seront pas accessibles aux
programmes définis en dehors de la classe xy : en plus de champs, une classe peut aussi
contenir des programmes. Pour créer un programme sur une classe paramétrée, il faut aussi
précéder la déclaration du programme par le mot template et la liste des classes susceptibles
d’être utilisées. Exemple : calcul du distance entre deux points
Pour calculer la distance de deux points, nous écrivons dans un fichier appelé xy_dist.cpp
Nous supposons que le fichier number.h contient les programmes d’affichages des nombres
Le nom put vient du nom du programme d’affichage en Ada : nous avons commencé à écrire
des programmes en Ada avant de basculer en C++, en utilisant intensivement le fameux
copier–coller ! De même, le nom item est issu du Manuel de Référence de Ada 95.
27
2ème PARTIE. STRUCTURES DE DONNÉES
Tableau
Un vecteur (ou un tableau à une dimension) est une fonction, au sens mathématique du terme,
dont l’ensemble de départ est un intervalle d’entiers (ou de réels en virgule fixe ou même de
caractères)
v : [1..7]*ent ;
Comme une fonction v peut être définie par l’ensemble des couples (i;v(i)) nous pouvons
écrire
v = ((1;5);(2;1);(3;2);(4;5);(5;0);(6;6);(7;3)) ;
Ces couples peuvent être mis côte à côte dans la mémoire dans l’ordre ci–dessus. v est
l’adresse du premier couple
v 47 1 5 2 1 3 2 4 5 5 0 6 6 7 3
Il devient inutile de mémoriser aussi les éléments de départ (les indices) puisque l’on peut
calculer facilement l’adresse des images
V = ( 5 ; 1 ; 2 ; 5 ; 0 ; 6 ; 7 )
1 2 3 4 5 6 7 i
v 47 5 1 2 5 0 6 3 v(i)
47 49 51 53 55 57 59 adresse
v : [a..b]*t ;
adr v ( i ) = v + ( i – a ) * taille t ;
où taille t est le nombre d’octets (ou de bits) occupés par un objet de type t.
v [ 4 ] = 9 ; v 5 1 2 9 0 6 3 x 2
u = v * 2 ; u 10 2 4 18 0 12 6
v = v + u ; v 15 3 6 27 0 18 9
28
Attention ! La fonction sizeof est applicable dans le programme où le tableau a été
déclaré mais elle n’est plus utilisable si le tableau est passé en paramètre ! Il faudrait
alors mémoriser cette taille dans un champ
Exercices :
integer m[3][7] =
1 2 3 4 5 6 7
{ { 5, 1, 2, 5, 0, 6, 3 },
5 { 110,22, 54, 018, 60, 12,106 },
2 -1 18 1
0 12 65 1 2 5 0 6 3
{ 0, 0, 0, 0, 0, 0, 0 } } ; 2 10 2 4 18 0 12 6
3 0 0 0 0 0 0 0
1 2 3 4 5 6 7
m [ 2 ][ 3 ] = (-1) ; 1
5 1 2 5 0 6 3
2
10 2 -1 18 0 12 6
3
0 0 0 0 0 0 0
Pour mémoriser une matrice nous pouvons le conserver ligne par ligne
5 10 0 1 2 0 2 -1 0 5 18 0
Exercices :
Liste chaînée
Une liste est un groupe d’élément de même type tel que chaque élément possède une valeur,
un seul successeur (sauf le dernier) et un seul prédécesseur (sauf le premier). Le successeur
(ou le prédécesseur) d’un élément est différent de celui d’un autre élément.
29
5 1 2 5 9
Dans une liste chaînée, chaque élément possède l’adresse de l’élément suivant (et,
éventuellement, celle de l’élément précédent). La liste proprement dite est l’adresse du
premier élément
liste<integer> *l ; l
x = l ; l 5 1 2
y = x->info ;
x y 5
x = x->alt ; l 5 1 2
y = x->info ;
x y 1
liste<integer> l
ce qui aurait évité d’écrire un * à chaque déclaration d’une liste. Malheureusement, C++ ne
permet pas d’utiliser un pointeur sur une classe. La seule solution serait d’écrire une classe
avec un champ unique
30
template<class t> info alt
void ins1(t x ; liste<t>* l ){ l 5 1 2
liste<t>* p ; t
p
liste<t>* p an
l t
pp->info
= new liste<t> 5
aih 1 2
= x; ; an
aT
p 0
aih
,"i
aTt
l 5
an 1 2
p->alt = l ; an
,"i
p = new liste<t> p a")
0
aih
an
;tt
aT
a")
l 5 1 2
l=p;}; an
,"i
;t
1ltemplate<class t> p 0
aih
an
void ins1(t x ; liste<t>* l ){ aTt
a")
liste<t>* p ; an
,"i
;t
p = new liste<t> ; aih
an
aT
a")
,"i
;t
an
a")
;t
Exercices :
31
Liste contiguë
Si le nombre maximum d’éléments d’une liste est connu d’avance, alors cette liste peut être
mémorisée dans un tableau
Mais C++ impose que le paramètre n soit une constante. Nous n’utilisons donc pas ce
paramètre mais indiquons la taille du tableau lors de l’initialisation
L’insertion d’un élément en tête de liste entraîne le décalage vers la droite de tous les
éléments de la liste
0 1 2 3
void ins1( t x ; liste_<t> l ) { A H
for( i=l.queue ; i>=0 ; i-- ) {
A H
l.val[i+1] = l.val[i] ; }
l.val(1) = x ; } T A H
Notez que nous utilisons le même nom liste pour identifier une liste chaînée ou une
contiguë. Mais le type liste contiguë possède deux paramètres contre un seul pour le type liste
chaînée, ce qui permet de les distinguer. On dit que le nom liste est surchargé ou
polymorphe. La surcharge permet d’éviter une profusion de nom ( liste_chainee,
liste_contiguë, etc.). Si le nombre d’argument est le même, nous pouvons encore définir
tan(double) et tan(xy<t>) par exemple.
Si le langage utilisé ne possède pas de pointeurs, une liste peut être aussi mémorisée dans un
tableau de couples info et alt. Le champ alt contient l’indice de l’élément suivant
info alt
0
1 D 3
2
3 Q 6
4 C 1 début
5
6 S -1 fin
7
32
Texte
Un texte est une liste de caractère mais un littéral texte commence et se termine par des
guillemets et les éléments de la liste ne sont pas séparés par une virgule. Un texte peut être
mémorisé dans une liste chaînée
liste<char>* t t
t = "Tah" t T a h
Si nous connaissons le nombre maximum de caractères d’un texte, nous pouvons utiliser une
liste contiguë
liste_cont<char> t t
init ( t, 10 ) ;
1 2 3 4 5 6 7 8 9 10
t = "alpha" ; t 5 a l p h a
Cette dernière fonction est appelée concaténation de 2 textes. On peut aussi utiliser un
caractère spécial qui ne doit jamais apparaître dans un texte pour marquer la fin de la liste. Le
langage C utilise le caractère NUL en ASCII (caractère '\0') et il n’y a pas de type texte mais
seulement un tableau de caractères
0 1 2 3 4 5 6 7 8 9 10
t
char t[10] = "alpha";
t a l p h a
Comme un texte est un tableau, l’emploi de littéral texte est limité à l’initialisation lors de la
déclaration. Un texte peut aussi être mémorisé dans un pointeur sur un tableau qui sera
redimensionné s’il devient trop petit ou trop grand. Dans ce cas, on peut utiliser un littéral
texte
char* t ; t
t = "Tah" ; t T a h
t = "Tahiana" ; t T a h i a n a
Les programmes
33
' #include <mbstring.h>
unsigned char *_mbscpy(unsigned char *dest,const unsigned char *src)
copient chaque caractère d’un texte dans un autre. Pour afficher un texte, nous n’avons plus
besoin de texte de formatage
printf ( tc )
printf ("Tah")
Les programmes
comparent deux textes. Les deux textes sont comparés caractère par caractère jusqu’à ce que
l’un des deux textes soit terminé ou que les deux caractères soient différents. Le texte plus
court est plus petit. Le résultat est négatif si s1<s2, nul s’ils sont égaux, positif si s1>s2.
Les fonctions
retournent le nombre de caractères d’un texte, non compris le caractère '\0'. Exemple :
comparaison de 2 textes. Nous comparons chaque caractère des 2 textes jusqu’à ce que l’un
des 2 textes soit terminé ou que les 2 caractères soient différents
34
if ( i > t2.queue ) {
y = false ; /*t2 plus court*/
} else {
if ( t1(i) < t2(i) ) {
y = true
} else {
y = false
}
}
} ; return y
}
Exercices :
- concaténation de 2 textes
- suppression du premier caractère d’un texte
- calcul du texte inverse d’un texte donné
- déterminer si un texte est égal à son inverse (texte palindrome)
- suppression des blancs (espaces) en début d’un texte
- suppression des blancs en début et à la fin d’un texte et transformation d’une suite de
blancs à l’intérieur du texte en un seul
Le programme principal peut avoir des paramètres qui sont des littéraux textes avec ou sans
guillemets séparés par des espaces. Les guillemets sont obligatoires si le paramètre possède
des espaces. Ces paramètres sont mémorisés dans un tableau spécial appelé argv. Le nombre
de paramètres entrés par l’utilisateur (y compris le nom du fichier) est mémorisé dans un
entier spécial appelé argc. Exemple : pour calculer la distance entre deux points quelconque
Nous supposons que le fichier number.h contient les fonctions de conversions de texte en
nombre réels
Pour lancer le fichier xy_dist_arg.exe, nous écrivons son nom et les paramètres séparés par
des espaces
35
2.82842712474619
C:\Documents and Settings\Tah>_
Fichier
Un fichier est un ensemble d’octets mémorisés sur un disque. En général, un disque est divisé
en « clusters » composés de 128, 256, 512, 1024, 2048 ou 4096 octets. Si un fichier est plus
petit qu’un cluster, il y a bien entendu des pertes de places. Si un fichier est plus grand qu’un
cluster, il est mémorisé sur plusieurs clusters qui ne sont pas forcément placés côte à côte sur
le disque. Grosso modo, on peut considérer un fichier comme un tableau d’adresses de
clusters (accès direct) ou une liste chaînée de clusters (accès séquentiel). De plus, il existe
deux types principaux de fichiers : les fichiers binaires sur lesquels les nombres sont
mémorisés en base 2 et les fichiers textes sur lesquels les nombres sont mémorisés sous
forme de littéral texte 1.234E-5. Pour créer ou ouvrir un fichier binaire appelé fich.bin,
nous écrivons
le paramètre "wb" indique que le fichier sera ouvert en mode écriture (« write ») et en mode
binaire. Pour ajouter des octets à la fin du fichier, on utilise "ab" (« append »). Une fois
ouvert, un fichier possède un pointeur positionné sur le premier octet du premier cluster. À
chaque fois que l’on écrit, en mode séquentiel, des octets sur le fichier, ce pointeur est avancé
selon le nombre d’octets écrits. Pour écrire un nombre (entier ou réel) en mode binaire nous
écrivons
&a est l’adresse en mémoire centrale du nombre, sizeof(a) donne le nombre d’octets à
écrire, le 1 est le nombre de réels à écrire. L’écriture d’un texte ou d’une liste chaînée est un
peu plus compliquée : il faut d’abord écrire la longueur du texte, puis les caractères qui le
compose
Le n+1 est dû au caractère '\0' qui termine les textes en C++. Comme un texte est une
adresse, nous n’écrivons plus &a. La lecture d’un nombre se fait par
36
void read ( FILE* fs, char* &a ) {
integer n
fread (&n, sizeof(n), 1, fs )
a = new char[n]
fread (a, n+1, 1, fs)
}
Pour créer ou ouvrir un fichier appelé fich.txt en mode texte nous écrivons
L’écriture en mode texte d’un nombre ou d’un texte se fait via fprintf qui utilise les mêmes
chaînes de formatage que printf
Liste triée
Une liste triée est une liste telle que chaque élément est inférieur (ou supérieur) ou égal à son
successeur. L’insertion dans une liste triée est
La suppression d’une valeur dans une liste triée, si elle s’y trouve, est
37
if ( p->alt = NULL ) break
if ( x <= p->alt->info ) break
p = p->alt
}
if ( p->alt <> NULL ) {
p->alt->info = x
del1 ( p->alt )
}
}
}
}
Exercices :
- fusion de deux listes triées (la liste obtenue doit être triée)
- intersection de deux listes triées
- recherche de l’adresse de la 1ère occurrence d’une valeur donnée dans une liste chaînée
triée (NULL si elle ne s’y trouve pas)
- recherche de l’adresse d’une occurrence d’une valeur donnée dans une liste contiguë
triée
Liste bidirectionnelle
Dans une liste chaînée bidirectionnelle, nous mémorisons aussi l’adresse du prédécesseur de
chaque élément. La liste elle-même est constituée par l’adresse du premier élément et celle du
dernier pour accélérer les insertions en fin de liste ou les parcours en ordre inverse
38
Pile
Une pile est une liste telle que le dernier élément inséré sera le premier supprimé (Last In –
First Out ou LIFO)
double x ;
pile<double> p ; p = NULL ; p
ins ( 5 ; p ) ; p 5
ins ( 1 ; p ) ; p 1 5
ins ( 2 ; p ) ; p 2 1 5
del ( x ; p ) ; p 1 5 x 2
ins ( 9 ; p ) ; p 9 1 5 x 2
del ( x ; p ) ; p 1 5 x 9
Exemple : traitement des éléments d’une liste chaînée dans l’ordre inverse :
donnera
3 2 1
On parcourt d’abord la liste de « gauche à droite » tout en mettant les éléments rencontrés
dans une pile. Il suffit ensuite de dépiler ces éléments, un par un
Le parcours en sens inverse en utilisant un programme récursif est plus simple (un
programme récursif est un programme qui contient un appel à lui–même)
39
put_inv ( l->alt )
put ( l->info )
}
}
Attention ! Pour une liste de grande taille, ce programme déclenche une mémoire
insuffisante parce que Windows semble réserver un espace limité pour chaque
application.
Arbre
Un arbre est un groupe d’éléments de même type tel que chaque nœud possède plusieurs
successeurs (sauf les feuilles) et un seul prédécesseur (sauf la racine). Les fils (ou le père)
d’un élément sont différents de celui d’un autre élément. Chaque nœud dispose de la liste de
ses successeurs
L’expression « infixée »
f ( x ) * ( 3 / 5 ) + g ( 5 )
+ ( * ( f ( x ), / ( y, 5 ) ), g ( 5 ) )
a + * f x
/ y
info suiv
info g 5
alt
Une forêt est une liste d’arbre (comme d’habitude, on devrait écrire
40
liste<arbre<t>*>* tete ; }
41
a + * f x
/ y
info suiv
info g 5
alt
f g 3
* 8
File
Une file est une liste telle que le premier élément inséré sera également le premier supprimé
(First In – First Out ou FIFO)
integer x ;
file<integer> f ; f = NULL ;
ins ( 5 ; f ) ; f 5
ins ( 1 ; f ) ; f 5 1
ins ( 2 ; f ) ; f 5 1 2
del ( x ; f ) ; f 1 2 x 5
ins ( 9 ; f ) ; f 1 2 9 x 5
del ( x ; f ) ; f 2 9 x 1
Une file peut être représentée par une liste chaînée, mais on mémorise également l’adresse de
la dernière cellule pour faciliter les insertions
42
tete queue
f
5 1 2
File contiguë
Le meilleur moyen serait d’utiliser un vecteur « circulaire » : lorsque la queue (ou la tête) de
la file atteint la fin du vecteur, elle repart au début
t q
f 2 5 4 7
ins ( 8 ; f ) ; q t
f 8 2 5 4 7
43
f.queue = 0 ; f.tete = 0
} else {
if ( f.queue = f.card ) {
f.queue = 0
} else {
f.queue = f.queue + 1
}
}
f.val[f.queue] = x
}
template <class t>
void del ( t &x ; file_<t> &f ) {
x = f.val[f.tete] ;
if ( f.queue = f.tete ) {
f.tete = -1 ; f.queue = -1
} else {
if ( f.tete = f.card ) {
f.tete = 0
} else {
f.tete = f.tete + 1
}
}
}
Anneau
Un anneau est une liste telle que le successeur du «dernier» élément est le «premier» élément
5 1 2 5 9
44
l
5 1 2
Arbre binaire
Un arbre n–aire est un arbre tel que chaque nœud possède au plus n successeurs. Un arbre 2–
aire est aussi appelé arbre binaire. Nous pouvons « intégrer » la liste des successeurs dans le
nœud
2
1
0
a 5
7 5
5
45
arbre2<t>* val ; bool ter ; }
template <class t>
put_post ( arbre2<t>* a ) {
pile<ab<t> >* p = NULL ;
arbre2<t>* x = a
ab<t> w ;
while ( true ) {
while ( x <> NULL ) {
w.val = x ; w.ter = false
ins ( w ; p ) ;
x = x->s1
} else {
while ( true ) {
if ( p = NULL ) return
if ( sommet(p).ter = 0 ) break ;
del ( p ; w ) ;
put ( w.val->info )
}
p->info.ter = 1 ;
x = p->info.val->s2
}
}
}
Récursion
L’exécution d’un programme récursif peut être considérée comme le parcours d’un arbre.
void p(t x) {
if (c(x)) {
a0(x)
} else {
a1(x) ; p(f1(x)) ;
a2(x) ; p(f2(x)) ;
'...
am(x) ; p(fm(x)) ;
am1(x)
}
où c(x) est une certaine condition, a0(x), a1(x), … am1(x) sont des actions
éventuellement complexes, mais n’entraînant pas d’appel récursif de p, et f1(x), f2(x),…
fm(x) sont des expressions dépendant de x.
46
f1 a0
a1
f2
a2 a0
am f1 a0
fm a1
f1 am f2
a2 a0
1
am fm
a0
am
a1
f2 1 f1 a0
a2 a0 a1
am a2 f2 a0
am a0 f1 am fm
fm f1 a0
1 am
a1 a1
f2 f2 1
a2 a2 a0
am am fm
am fm am a0
1 1
a0
Nous devons mémoriser dans une pile le paramètre x et le numéro i du prochain « fils » à
visiter
p = NULL ;
while ( true ) {
if (!c(x)) {
a1(x) ; ins(p,(x,2)) ; x=f1(x)
} else {
a0(x) ;
{
if ( p = NULL ) return
del(p ; (x ; i)) ;
if ( i < m+1 ) break ; am1(x) ; }
if ( i = 2 ) {
a2(x) ; ins(p,(x,3)) ; x=f2(x)
...
} else if ( i = m ) {
am(x) ; ins(p,(x,m+1)) ; x=fm(x)
}
}
}
Un arbre binaire ordonné est un arbre binaire tel que l’information d’un nœud est supérieure
ou égale à celle de son 1er fils et inférieur à celle de 2ème fils (s’ils existent). Un arbre binaire
ordonné peut être dégénéré : la plupart des nœuds n’ont qu’un seul fils
a 9 4
7 3
6 2
1
Un arbre binaire équilibré ou arbre AVL (dû à Adelson, Velskij et Landis) est un arbre
ordonné tel que toutes les feuilles soient situées à un rang log2n ou log2n+1 où n est le
47
nombre d’éléments de l’arbre. Si le déséquilibre est dû au 1er sous–arbre du 1er sous–arbre
d’un nœud
h+1 h+1
1 1
b1 2 b1 2
3 3 3 3
b 4 b 4
5 h 5 h
b2 6 b2 6
7 7
a 8 a 8
9 h 9 h
a2 10 a2 10
11 11
11 11
h h
b1 1 b1 1
2 2
b 3 b 3
4 h+1 ou h 4
c1 4 c1 4
5 c 5
c
6 7 6
7
c2 c2
a 8 8
a
9 h
9 h
a2 9
a2 9
11
11
12
12
Tri rapide
Ce petit problème n’entraîne pas la création d’une structure de donnée mais sa généralisation
nous a forcé sa présentation dans cet ouvrage. Soit un vecteur v de n éléments
« ordonnables » (des nombres ou des textes par exemples). Le problème consiste à trier ce
vecteur : une méthode simple serait de choisir un élément quelconque appelé pivot, mettre
48
tous les éléments inférieurs à ce pivot à gauche et tous les éléments supérieurs à droite, puis
recommencer avec ces deux sous–listes.
Lorsque la taille d’une sous-liste est assez faible (15 ou 16 éléments [Meyer 78]), il faut
arrêter la partition parce que les appels récursifs sont trop lourds, puis lancer un tri par
insertion qui est très efficace sur un vecteur presque ordonné.
Exercices :
- le kème plus petit : sans trier le vecteur v, chercher l’indice k tel que les éléments
v(1)..v(k) soient inférieurs ou égaux à v(k)
- le drapeau français : soit un vecteur composé de n éléments bleu, blanc ou rouge.
Écrire le programme qui met tous les éléments bleus à gauche, les blancs au milieu et
les rouges à droite du vecteur
Tas contigu
Un arbre binaire complet est un arbre tel que chaque nœud (sauf les feuilles) possède 2 fils.
Un arbre complet peut être mis dans un vecteur. Si on remplit le vecteur en faisant un
parcours en largeur de l’arbre alors
s1 i = i * 2 ;
s2 i = i * 2 + 1 ;
père i = i ÷ 2 ; /* 9 ÷ 2 = 4 */
Un tas contigu est un arbre binaire complet tel que l’information d’un nœud est supérieure
(ou inférieur) ou égal à celles de ses fils et que certaines feuilles (les dernières dans le vecteur
le représentant) peuvent être absentes
49
4
7
5
8
3
6
2
9
0
1
1 2 3 4 5 6 7 8 9 10 11 12 13 14
9 8 9 7 6 1 3 4 5 3 2 0
L’insertion dans un tas contigu consiste à mettre l’élément à insérer à la fin du vecteur, puis à
l’échanger avec son père si nécessaire, puis avec son grand-père, etc.
La suppression de la racine d’un tas contigu consiste à mettre le dernier élément à cette 1 ère
position puis à l’échanger éventuellement avec le plus grand de ses fils : un tas est une file
triée dont les insertions et les suppressions se font en log(2 ; n) si n est le nombre
d’éléments du tas.
Alternantsuccesseur
Une forêt est une liste d’arbre. Un alternant–successeur est une forêt telle que chaque nœud
dispose de l’adresse de son « fils aîné » et de son « frère »
50
a 5 1 2 9
5 3
suiv
info 3
alt
9 5
4 3 7
Dictionnaire
Un dictionnaire peut être mémorisé dans un alternant–successeur trié : les informations sont
des caractères et chaque « mot » est terminé par un caractère inférieur à toutes les lettres (par
exemple, un espace )
51
a I R È N E Sainte du 5 avril
N I Négation
R E I N Viscères doubles
E Souveraine
Exemple : insertion d’un mot dans un dictionnaire. Le mot est terminé par un nil
52
a = adr ( *m, a, reste ( m+1, d ) )
} else if ( *m = a->info ) {
ins ( m+1, d, a->suiv.suc )
} else {
ins ( m, d, a->alt )
}
}
}
Arbre basique
Si les « mots » d’un dictionnaire sont des nombres en base quelconque, alors il peut être
mémorisé dans une forêt contiguë : les successeurs d’un nœud sont placés dans un vecteur et
le champ info disparaît. Si les nombres sont de longueurs différentes (chiffres après virgule),
il faudrait un bit supplémentaire pour distinguer la fin d’un « mot »
001
002
01
0 020
1
2 022
210
211
212
Table
Une table est une fonction (ou relation, au sens mathématique du terme) qui à une
information appelée clé associe une autre information appelée attribut
particip°
Tana
70.4
Maj
52.1
Fianar
49.6
Diégo
65.7
Tam
53.8
Tul
p1 attributs
clés
53
Toutes les clés doivent être différentes. On peut mémoriser ( « indexer » ) les clés dans un
arbre ordonné ou mettre la table dans une simple liste de clés et d’attributs
s = 0 ;
for ( x = l ; x <> 0 ; x = x->alt ) {
s = s + x->info ;
}
m = s / card t ;
Graphe
Un graphe est un groupe d’éléments tel que chaque nœud possède plusieurs successeurs (sauf
les points de sortie) et plusieurs prédécesseurs (sauf les points d’entrée)
3
info suiv
g 2
info
alt 2
Le moyen le plus simple pour entrer un graphe serait un logiciel comme l’outil Dessin de
Excel. Sinon il faudrait donner un identificateur (par exemple un texte) pour chaque nœud et
mettre dans une table la liste des identificateurs de ses successeurs
54
template<class id, class tn>
class attr_n { public :
tn info_n
liste<avl<id,attr_n>*>* suiv ; }
template<class id, class tn>
class graphe { public :
avl<id,attr<id,tn> >* val
liste<avl<id,attr_n<id,tn> >*>* entree ; }
F
graphe<char*,integer> r C
init ( r ) ; 2 K
1
ins("A","C",r);ins("A","D",r) 9
ins("B","G",r);ins("B","E",r) A I
ins("C","F",r);ins("C","J",r) 5 D 3
ins("D","G",r)
ins("E","G",r);ins("E","H",r) 8 L
ins("F","K",r);ins("F","I",r) B G 6
ins("G","H",r); 0 7 J
ins("H","J",r);ins("H","M",r); E 5
ins("I","K",r);ins("I","L",r)
2 M
ins("I","I",r)
ins("J","L",r);ins("J","M",r) 5 4
H
Exemple : calcul du rang de chaque nœud. Le rang d’un nœud est le plus petit nombre d’arcs
qui le sépare des points d’entrée. Pour calculer les points d’entrées, nous détectons les nœuds
dont le nombre de pères est égal à zéro. Nous mettons le résultat dans info_n. Il suffit de
balayer l’AVL et d’ajouter un 1 au nombre de père des successeurs d’un nœud
55
y : par ( ti * ent ) ; g : par graphe t ;
y = nbpère g {
f : file ( graphe t ) = NULL ; /*adresse d'un nœud en fait*/
y = NULL ;
x :: pt_entrée g { /*parcours en tant que liste*/
y ( x ) = 0 ;
ins ( x ; f )
{
f = NULL ) break
del ( x ; f ) ;
s :: suiv x {
s y {
y ( s ) = y ( s ) + 1
} else {
y ( s ) = 1 ; ins ( s ; f )
Graphe étiqueté
Un graphe étiqueté est un graphe tel que chaque arc possède aussi une information
F
C
3
2 6 6 K
9
3 3
A I 3
5 4 3
D
4 9 3
2 L
B 2 G 1 10
2 5
10 6 6 J 2
E 5
2 2 1
2 3
3 M
2 3 9
H 6
graphe(ta;t) =
*adr( info:t ; suiv:*(étiq:ta;nsuiv:graphe(ta;t) )
On peut aussi mettre les attributs des arcs dans une table. Lorsque tous les nœuds peuvent
être des points d’entrée, la représentation dans une table s’impose. Exemple : calcul du plus
court chemin entre 2 nœuds a et z ( produire une liste de nœuds )
réseau(ti;ta;t) =
ti*( info:t ; suiv:*(étiq:ta;nsuiv:ti) )
y : par *ti ; r : réseau(ti;ta;t) ; (a;z) : par 2*ti
ta1 : par type ; dist : fonction ta1
y = pcm ( r ; a ; z ; dist ) {
f : ta1*ti = NULL ; /* "file" d’attente*/
prec : ti*ti = NULL ;
da : ti*ta1 = NULL ; /*distance par rapport à a*/
x = a ; da a = 0 ; az : ta1 = ∞ ; ax : ta1 = 0 ;
{
s :: suiv r x { ns = nsuiv s ;
as = ax + dist s ;
as < az { /*recherche dans un cercle de rayon az*/
ns da ou_bien da(ns) > as {
da ns = as ; prec ns = x ;
ns = z {
56
az = as ;
} else {
ins ( as ; ns ; f ) /* si s f */
f = NULL ) break
del_min ( ax ; x ; f )
y = ()
x = z ; { /*inverser la "liste" prec */
x = a ) break
inst ( prec x ; y ) ; x = prec x
Pour accélérer l’algorithme, nous pouvons, lors de l’insertion d’un élément dans f :
- le chercher d’abord en se basant sur l’ancienne distance et (en cas d’égalité)
l’identificateur du nœud,
- ensuite supprimer cet élément
- et enfin, insérer la nouvelle distance.
Processeur
Un processeur est un ensemble de circuits de calcul, de registres, d’indicateurs, etc. qui peut
exécuter des instructions. Nous supposons par la suite que nous disposons d’un ordinateur
possédant des processeurs qui peuvent lire au même moment une même partie de la mémoire.
Processeurs
Bus d’adresse
3
Bus de données
2
0 1 2 3 4 5 6 7…
Mémoires
Même si l’ordinateur ne possède qu’un seul processeur, celui–ci peut simuler un multi–
processeur en exécutant un des programmes pendant un très court laps de temps avant de
simuler un autre « processeur » (thread dans le vocabulaire Windows). En C, on lance un
thread par le programme
57
start_address est le nom du programme à exécuter par le processeur, stack_size est la
taille, en octets, de l’espace (la pile des appels) à utiliser par le processeur (on se demande
comment peut–on connaître une telle quantité ?), arglist est l’unique paramètre de
start_address. Il doit être présent, mais il peut être NULL si start_address n’a pas de
paramètres. Si start_address a besoin de plusieurs paramètres, on peut toujours définir un
struct et passer l’adresse de ces structures comme argument. La valeur retournée est un entier
appelé identificateur du thread. Le thread s’arrête quand le programme principal s’arrête, ou
si le thread a atteint la fin de start_address, on s’il rencontre le programme
Exemple 1 : soit un processeur principal qui lance quelques « fils ». Ces fils affichent
simplement leur numéro.
Exemple 2 : cinq philosophes passent leur temps à méditer. Nature oblige, il doivent manger
de temps en temps. Ils sont assis autour d’une table sur laquelle est placée un grand bol de
spaghettis. Chaque philosophe possède une assiette et une fourchette. Mais les spaghettis sont
tellement difficiles à manipuler, qu’il faut deux fourchettes pour les manger. Un philosophe
peut emprunter une fourchette à son voisin de gauche ou de droite. Écrire le programme qui
assure qu’une fourchette n’est pas utilisée en même temps par deux philosophes.
58
unsigned char ti_sec; /* seconds */
};*/
void delay ( double d ) { ' attente pendant d secondes
struct time t1, t2
gettime ( &t1 )
while ( true ) {
gettime ( &t2 )
if ((asSec(t2)-asSec(t1))>d) break
}
}
void enfant(void *x0) {
no_id *x = (no_id*) x0
integer d
integer i = x->id
'----------------------
while ( true ) { ' boucle infinie, c'est le Main qui arrête tout
while ( x->f[i] <> 2 ) {} ; ' attendre 2 fourchettes
d = random ( 10 ) ; ' nombre aléatoire entre 1 et 10
delay ( d ) ; ' manger pendant d secondes
x->f[i] = 0
' passer les fourchettes aux voisins :
if ( i = 0 ) { x->f[4]++
} else { x->f[i-1]++ ; }
if ( i = 4 ) { x->f[0]++
} else { x->f[i+1]++ ; }
}
}
integer main(void){
integer i
integer t[5] ; ' nombre de fourchettes par philosophe
no_id *x
'-----------------------
randomize () ; ' initialis° des nombres aléatoires
for (i = 0; i < 5; i++) { ' une fourchette pour chacun d'abord
t[i] = 1 ; }
for (i = 1; i < 5; i+=2) {
t[i] = 0 ; ' pas de fourchette pour 1 et 3
t[i-1] += 1 ; ' 2 fourchettes pour 0 et 2
}
for (i = 0; i < 5; i++){
x = new no_id
x->f = t ; x->id = i
_beginthread(enfant,4096,(void *)x)
}
for ( integer j = 1 ; j <= 60 ; j++ ) {
put ( j ) ; put ( "\t: " )
for ( integer k = 0 ; k < 5 ; k++ ) {
put ( t[k] ) ; put ( " " )
} ; put ( "\n" )
delay ( 1 ) ; ' affichage de t toutes les secondes
}
}
Ce programme doit être compilé avec l’option -tWM. Ne sachant pas comment entrer une telle
option via l’IDE de Borland C++, nous écrivons dans l’invite de commande
cd c:\tah\c\debug\source\cours
bcc32 -tWM c_philo.cpp
59
Ne sachant pas comment indiquer où se trouve les fichiers d’en–tête .h, nous écrivons leur
nom complet.
Hypercube
Un hypercube de dimension n est un réseau formé de 2n nœuds. En reliant les nœuds de deux
hypercubes de dimension n-1 dont les numéros en base 2 ne diffère que par un seul bit, nous
obtenons un hypercube de dimension n
0 1
10 11 010 011 110 111
n=1 n=2 n=3
Deux nœuds quelconques sont séparés par n liaisons au maximum. La recherche d’un chemin
est facile : il suffit de trouver un nœud libre dont le numéro diffère d’un seul bit par rapport
au numéro du nœud courant.
Prédicat
Un prédicat est une fonction qui retourne un bit avec un effet de bord : si un au moins des
paramètres est libre (vient d’être déclaré), alors ce paramètre recevra une valeur. Exemple :
conversion d’un nombre formé de chiffres en toutes lettres, et vice versa
/* fichier conv.pl */
conv(X,Y) :-
X='1', Y=un
X='2', Y=deux
X='3', Y=trois.
en supposant que le , équivalent à l’opérateur et, est évalué avant le ; équivalent à ou. Ce
programme peut être utilisé de 4 façons différentes
60
– connaissant un nombre en toutes chiffres, l’écrire en toutes lettres
conv('1',X).
X = un
conv(X,deux).
X = 2
– est–ce qu’un nombres en toutes chiffres est l’équivalent d’un autre en toutes lettres
conv('3',trois).
Yes
conv(X,Y).
X = '1'
Y = un ; ' ici, appuyez sur la touche point–virgule
X = '2'
Y = deux
X = '3'
Y = trois
p1(x) ; p2(x)
est vrai si l’une au moins de ces alternatives est vraie. L’évaluation de cette instruction se fait
comme suit :
– si p1(x) est vrai alors l’évaluation se termine par un succès
– sinon, on passe à l’alternative suivante, p2(x)
– si p2(x) est un succès alors l’évaluation se termine par un succès
– sinon l’évaluation se termine par un échec (il n’y a plus d’autres alternatives)
p1(x) , p2(x)
est vrai si les deux buts p1(x) et p2(x) sont tous vrais. L’évaluation de cette instruction se
fait comme suit
– si p1 x est faux, p2 x ne sera pas évalué, le résultat est faux
– si p1 x est vrai, p2 x sera évalué
– si p2 x est vrai alors le résultat est vrai
– si p2 x est faux,
– si x n’est pas libre, le résultat est faux
– sinon, p1 x est réévalué pour d’autres valeurs de x (backtrack)
– si on ne trouve pas d’autres valeurs de x, le résultat est faux
– sinon, p2 x sera évalué, etc.
61
Clause
La définition d’un prédicat formé de plusieurs alternatives peut être repartie sur plusieurs
clauses et nous retrouvons l’équivalent d’une table
Les clauses dont la partie droite est vide sont des faits. Le prédicat fail provoque un retour
en arrière (équivalent à 1=2). write(X) affiche X à l’écran. Ajoutez les lignes suivantes au
fichier conv.pl
conv.
X = 1, Y = un
X = 2, Y = deux
X = 3, Y = trois
No
conv:-
conv(X,Y),!,
write('X = '), write(X),
write(', Y = '), write(Y), write(' ;\n'),fail.
donnera
conv.
X = '1', Y = un
No
donnera
62
conv.
X = 1, Y = un
X = 2, Y = deux
No
Pour ajouter une clause avant toutes les clauses existantes d’un prédicat donné, il faut d’abord
indiquer que le prédicat est dynamique
:-dynamic(conv/2).
conv ( '1', 'un' ).
conv ( '2', 'deux' ).
conv ( '3', 'trois' ).
conv:-
conv(X,Y),
write('X = '), write(X),
write(', Y = '), write(Y), write(' ;\n'),fail.
conv/2 indique que c’est le conv avec deux paramètres qui est dynamique.
asserta(conv('0', 'zero')).
Yes
conv.
X = 0, Y = zero
X = 1, Y = un
X = 2, Y = deux
X = 3, Y = trois
No
L’insertion d’une clause après toutes les clauses existantes se fait par
assertz(conv('4', quatre)).
Yes
conv.
X = 0, Y = zero
X = 1, Y = un
X = 2, Y = deux
X = 3, Y = trois
X = 4, Y = quatre
No
retract(conv('1',_)).
Yes
conv.
X = 0, Y = zero
X = 2, Y = deux
X = 3, Y = trois
X = 4, Y = quatre
No
63
Le soulignement _ est une variable anonyme, et indique que sa valeur ne nous intéresse pas.
64
3ème PARTIE. PROBLÈMES
Vous êtes un chien, et un ami humain vient de lancer votre os favori dans le
jardin voisin par–dessus un grillage (…) Or, une barrière est ouverte dans le
grillage à environ quinze mètres de l’os. Que faites–vous ? Certains chiens
courront jusqu’au grillage et resteront là à aboyer. D’autres s’en éloigneront pour
aller jusqu’à la barrière ouverte et courir jusqu’à l’os tant désiré.
(Douglas HOFSTADTER – Gödel, Escher, Bach :
les Brins d’une Guirlande Éternelle)
– Tas dynamique [ Lignelet 90 ] : construire un tas dont la taille maximale n’est pas connue
d’avance
– Inclusion de deux polygones [ Sedgewick 91 ] : déterminer si un polygone donné est inclus
dans un autre polygone. Un polygone est une liste de points (ou un anneau de points).
– Transect : chercher les points d’intersections d’une droite (ou d’un ensemble de segments
de droite représenté par une liste de points) avec un ensemble de « courbes de niveau »
(des listes de points, chaque liste possédant une altitude).
– Permutation de distance minimale : produire toutes les permutations possibles d’un vecteur
donné en limitant la distance entre les éléments échangés
– Coloriage d’une carte [ Lignelet 90 ] : colorier une carte contenant des régions en utilisant
le minimum de couleurs possibles de telle façon que deux régions voisines soient de
couleurs différentes. La carte peut être représentée par un graphe : deux nœuds sont reliés
si les régions correspondantes sont voisines.
– Centre d’un graphe : déterminer le nœud d’un graphe tel que la distance qui le sépare du
nœud le plus lointain soit la plus petite possible.
65
– Triangulation de Delaunay [ Sedgewick 91 ] : le polygone de Voronoï d’un point « cible »
est l’ensemble des points de l’espace qui soient plus proche de lui que tout autre point
cible. Construire le graphe représentant ces polygones.
– Arbre 2D équilibré : étant donné un ensemble de points en 2D, construire un arbre qui
permet de trouver rapidement les points ayant une abscisse et une ordonnée donnés.
– Tracé d’un graphe planaire : tracer un graphe tel qu’aucun arc ne croise un autre.
– Traduction d’un nombre [ Gramm 86 ] : écrire un nombre en toutes lettres et vice versa
66
CONCLUSION
Mais il a fallu attendre Ada, mis au point en 1979 par une équipe dirigée par J. Ichbiach, pour
voir apparaître les types paramétrés (génériques dans le vocabulaire Ada) et le
polymorphisme qui permet d’éviter la profusion de noms : liste_chaînée,
liste_contigüe, etc. Des efforts ont été aussi fournis pour standardiser la programmation
parallèle, mais le langage souffre encore d’un manque de lisibilité et n’intègre pas la
programmation déclarative introduite par A. Colmerauer dans le langage Prolog ( «
Programming in Logic » ) en 1973. Les prédicats peuvent être vus comme une tendance vers
le polymorphisme, puisque le test d’égalité et l’affectation, par exemple, n’utilisent qu’un
seul identificateur.
Remarquons, par ailleurs que ces deux langages ont été inventés par des universitaires
français, soucieux d’écrire un logiciel « parfait », ils ont d’autres chats… cours à enseigner,
contrairement aux industriels américains qui se retrouveront au chômage le jour où ils
sortiraient le Graal des programmeurs, du genre combinant programmation parallèle et
déclarative avec type paramétré, liste(t:type):=adr(info:t,alt:liste(t)), et des
littéraux liste d’instruction, for x in l do put(x.info) end... Tel est le cas de C++,
inventé en 1983 par B. Stourstrup avec des « versions » qui n’en finissent plus...
67
BIBLIOGRAPHIE
Borland – Borland C++ 3.0 Programmer’s Guide. 1991, Scotts Valley Drive, CA.
Pratiquement équivalent à l’aide en ligne.
Borland – Turbo Prolog 0.5Owner’s Handbook. 1986, Scotts Valley Drive, CA.
Utile ne serait ce que pour apprendre les éléments communs à tous les compilateurs
Prolog.
Courtin J., Kowarski I. – Initiation à l’algorithmique et aux structures de données. Tome 2 :
récursivité. 1984, Dunod, Paris.
Semblable au présent ouvrage mais ne traite pas les graphes. Contient de nombreux
algorithmes sur la manipulation des listes et des arbres.
Lignelet P., Perrot J.F. – Structures de données avec Ada. 1990, Masson, Paris.
Contient, entre autres, les tas dynamiques, les tas avec préservation de l’ordre d’entrée
et le coloriage d’une carte avec 4 couleurs. Démontre en passant la toute–puissance
des types paramétrés du langage Ada.
68