Post on 03-Jan-2016
description
Algorithmique Procédurale
IUP GMI 1ère année
Denis Robilliard
SommaireIntroduction
Historique La machine de Turing Langages de programmation Arbres programmatiques et Ada Syntaxe, expressions régulières
Premier programme Premier programme en Ada Instructions
Commentaires Variables, affectations Types de base Opérations arithmétiques de base Entrées/sorties sur les types de base Inclure des bibliothèques Conversions de types
Structures de contrôle du flux d ’instructions Structure d'alternative AP / ADA Expressions booléennes, opérateurs relationnels
Structure de cas AP / ADA Expression statique ou dynamique Phases d'écriture d'un programme Structure de boucle AP /ADA Boucle "pour" AP / ADA Boucle avec sortie Résumé des différentes structures de boucle en
ADA Bloc déclaratif AP /ADA
Types tableaux Tableaux AP / ADA
Exemple sur les tableaux Règles, opérations et attributs de tableaux Les tranches Les agrégats Les chaînes de caractères AP / ADA Entrées / sorties sur les chaînes Attributs relatifs aux chaînes et aux caractères Les tableaux à plusieurs dimensions
Les sous-programmes : procédures et fonctions Paramètres et valeur de retour
Paramètres par défaut Position des procédures et des fonctions Déclarations préliminaires Portée et visibilité
RécursivitéRetour sur les types, enregistrements
Introduction
Algorithmique : "art" d ’écrire des algorithmes ou programmes.
Synonyme : programmation.Algorithme : procédé opératoire qui
permet, à partir de données, d ’obtenir un résultat précis.
Ex : algo de l ’addition de deux nombres.
Algo procédurale (ou encore impérative) : les programmes sont conçus comme une liste d ’ordres / instructions, qui doivent s ’exécuter un par un, dans un ordre chronologique défini à l ’avance.
Il existe d ’autres formes d ’algo : parallèle, objet, fonctionnelle, déclarative, ...
Historique
Premiers algorithmes dès l ’Antiquité : Euclide, Eratosthène, …
Premières machines à calcul : Pascal, Descartes (17ème), algorithme "codé" par des engrenages.
Premier véritable ordinateur : la machine de Babbage (19ème), non achevée. Nouveauté : elle est programmable.
Première personne à programmer : Lady Ada Lovelace, "assistante" de Babbage.
Théorisation : Turing, Church (années 30), définition de la notion mathématique de "fonction calculable" = ce qui peut être réalisé par un "ordinateur universel".
Premiers ordinateurs électroniques mis au point pendant la 2nde guerre mondiale.
La machine de Turing
Un "ordinateur" de papier : une bande de papier, finie mais aussi
longue qu ’on le souhaite, divisée en cases qui peuvent contenir les symbole 0 ou 1
une "tête" de lecture/écriture qui pointe sur une case, qui peut lire ou écrire un 0 ou un 1 dans la case, et qui peut se déplacer d ’une case à droite ou à gauche
… (voir page suivante)
un programme, composé de règles de la
forme : <q, s> ~ <q ’, s ’, p> avec {q, q ’} des états de la machine de
Turing, dont le nombre, toujours fini, est dépendant du programme que l ’on exécute
{s, s ’} dans {0,1} les symboles que l ’on peut écrire sur la bande
p dans {g,d} une direction indiquant vers quelle case voisine (gauche ou droite) se déplace la tête de lecture pour exécuter la prochaine instruction
Représentation de la machine de Turing0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Tête de lecture dans la configuration de départ
...
q0
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Position de la tête de lecture et état de la bande après application de la règle : <q0, 0> ~ <q1, 1, d>
...
q1
Exemple de programme Objectif : le programme va lire un nombre
en base 1 composé de 2 chiffres au plus. Il va écrire le double de ce nombre, en base 1, plus loin sur la bande.
Note : la bande sera donc divisée (virtuellement) en une zone de données de deux cases suivie d ’une zone de résultat. La donnée en entrée est écrite sur la bande par l ’utilisateur.
Le programme :<q0, 0> ~ <q10, 0, d><q0, 1> ~ <q11, 1, d><q10, 0> ~ <q20, 0, d><q10, 1> ~ <q21, 1, d><q11, 0> ~ <q21, 0, d><q11, 1> ~ <q22, 1, d><q20, 0> ~ <qF, 0, d><q21, 0> ~ <q311, 1, d><q311, 0> ~ <qF, 1, d>
<q22, 0> ~ <q321, 1, d><q321, 0> ~ <q422, 1, d><q422, 0> ~ <q523, 1, d><q523, 0> ~ <qF, 1, d>
Note : q0 est l ’état initial de la
machine qF est l ’état final : quand
la machine passe dans l ’état qF elle s ’arrête
Tout ce qui peut être calculé par une machine de Turing = ensemble des fonctions calculables
Tous les ordinateurs actuels sont des machines de Turing, à la restriction près que leur mémoire est finie.
Il existe des fonctions non calculables !!! Un ordinateur ne peut pas tout faire...
Langages de programmationPour communiquer l ’idée d ’un algo, à une
personne ou à un ordinateur, nécessité d ’un langage.
En général les langages informatiques sont universels (ils permettent de coder tous les programmes possibles).
Cependant, ils favorisent souvent une certaine approche des problèmes : Pascal, C : programmation procédurale Smalltalk, Eiffel, Java : programmation objet Prolog : programmation déclarative
Arbres Programmatiques et Ada
Nous utiliserons 2 langages : Un langage pédagogique, en partie graphique,
noté AP : les Arbres Programmatiques. Note : ce ne sont pas des organigrammes.
Un langage "de la vraie vie" : ADA. C ’est un langage de programmation procédurale, créé en 1983 sur appel d ’offre du Département de la Défense américain. Il possède, depuis 1995, des extensions objets, que nous n ’utiliserons pas.
Pourquoi les arbres programmatiques ? C ’est une notation "papier" (nous n ’exécuterons
pas un AP sur machine). Nous nous permettrons une syntaxe plus souple.
Les APs forcent le programmeur à une approche hiérarchique, descendante du problème.
C ’est la "programmation structurée" : On part des idées générales et on va
progressivement de plus en plus dans le détail. Les branches d'un arbre programmatique sont
assez indépendantes les unes des autres (au moins en ce qui concerne le déroulement des instructions).
La programmation procédurale structurée marche très bien pour concevoir de petites applications (- de 5000 lignes).
D ’autres approches, notamment la conception objet, seront vues plus tard dans le cursus pour les applis plus importantes. Note : le code de la navette spatiale
américaine fait plus de 1.000.000 de lignes...
Syntaxe, expressions régulières
Pas de langage sans syntaxe (ou orthographe) Nous utiliserons une forme restreinte des
« expressions régulières » pour définir précisément la syntaxe autorisée, particulièrement en Ada.
Exemple :identifiant ::= lettre { [ _ ] / lettre | chiffre / }
Ce qui signifie qu ’un identifiant commence forcément par une lettre et se continue par une suite, éventuellement vide, de lettres et/ou chiffres, qui peuvent chacun être précédé d'un "blanc souligné".
Symboles formant les expressions rationnelles ::= introduit l ’expression rationnelle qui définit la
syntaxe associée au concept; [ ] délimitent une sous-expression facultative; { } délimitent un motif qui peut être répété; … désigne un terme identique au terme précédent; | (barre verticale) "ou bien" dans une énumération; \ \ délimitent une série d'option d'options parmi
lesquelles il faut choisir une; en_gras dénote un mot réservé (mot clé) de ADA
ou un symbole à prendre littéralement.
Premier programme
sortir("le nombre est : ")sortir(nb)
bonjournb : entier
demander nombre afficher nombre
sortir("bonjour") sortir("entrer votre nombre : ")entrer(nb)
seq
saluer
Nom de l ’unité/programme
Déclaration devariable
Connecteur séquenced ’instructions
Nom de blocd ’instructions Bloc d ’instructions instruction
Premier programme en Ada
with text_io, integer_text_io;use text_io, integer_text_io;
procedure bonjour isnb : integer;
begin-- saluerput_line("bonjour");-- demander nombreput("entrez votre nombre : "); get(nb);-- afficher nombreput("le nombre est : "); put(nb);
end bonjour; -- le rappel du nom d'unité est facultatif
Instructions
Un programme est composé d ’instructions. Une instruction peut elle-même contenir d ’autres instructions.
Par défaut, les instructions s'exécutent en séquence.
En Ada les instructions sont terminées par un point virgule (facultatif en AP).
Une instruction peut s ’étendre sur plusieurs lignes.
Pas de différence entre majuscules et minuscules
Commentaires
Les commentaires ont pour objectif de faciliter la compréhension du programme.
Les noms de bloc d ’instructions AP seront toujours reportés comme commentaires en Ada. Des commentaires supplémentaires seront évidemment les bienvenus.
Les commentaires débutent par -- et se poursuivent jusque la fin de ligne.
Variables, affectation
Chaque donnée (nombre, caractère, …) que l ’on veut réutiliser ultérieurement doit être stockée dans un emplacement mémoire appelé "variable".
Les 2 langages, AP et ADA, sont "fortement typés" : une variable ne peut contenir qu ’une seule sorte ou "type" de données (soit un caractère, soit un nombre entier, …)
Les variables utilisées par un programme doivent être "déclarées" avant leur utilisation : on précise leur nom et l ’unique type de valeur qu ’elles peuvent contenir.
L ’affectation est une instruction. Elle sera noté := en AP et en ADA. nb := 2; -- range la valeur 2 dans nb
Types de base
Nous utiliserons 4 types de base entier (Ada : integer) : nombres sans
virgule flottant (Ada : float) : nombres avec
virgule. Un flottant s'écrit toujours :flottant ::= chiffre+ "." chiffre+ex : 0.0 0.5 123.45ctr-ex : .0 3 324.
Notation exponentielle pour les flottants1.23e2 = 1.23E2 = 1.23 * 10^2 = 123.0
caractère (Ada : character) : 'A', 'b', '?', ...
booléen (Ada : boolean) : valant Vrai ou Faux (Ada : True, False)
Opérations arithmétiques de base
Les 4 opérations + - * / Attention, sur des entiers le résultat est
arrondi à l ’entier le plus prochenb := 3 / 2; -- nb contient 1
Il est interdit de mélanger des types différents dans une opération quelconque nbf := 3.0 / 2; -- erreur de compilationnbf := 3.0 / 2.0; -- ok, nb vaut 1.5
Reste et modulo de la division entière de 2 nombres : rem, mod
nb := 3 rem 2; -- nb vaut 1nb := 10 mod 5; -- nb vaut 0
Exponentiation (entiers et flottants) : **nb := 3 ** 2; -- nb vaut 9fonctionne aussi sur les flottants
Valeur absolue (entiers et flottants) : abs()
nb := abs(-3); -- nb vaut 3
Entrées/sorties sur les types de base
Lecture (ou saisie) d ’une donnée AP : entrer (nom_de_variable) Ada : get (nom_de_variable);
Ada permet de spécifier le nombre maximum de caractères (largeur de champ) à prendre en compte : get (nom_variable, largeur_champ)
Le programme s ’arrête, l ’utilisateur tape une donnée au clavier suivie d ’un retour chariot.
Une donnée est toujours saisie dans une variable.
Ecriture (ou sortie) d ’une donnée AP : sortir (variable | donnée ) Ada : put (variable | donnée );
Ada permet de donner un format à la donnée : nombre de chiffres, …
entiers : put (variable | donnée , nb_chiffres);flottants : put (variable | donnée,
nb_chiffres_avant_virg , nb_chiffres_apres_virg , nb_chiffres_exposant);
Exemples de sorties put(123); put(12345678); put(123,2); put('A');
put('A'); -- écrit 123 12345678123AANote : le format par défaut des entiers est 8
caractères de large. Un format trop petit est ignoré.
put(123.456); -- écrit 1.23456E2 put(123.456, 2, 2, 0); -- écrit 123.45Note : par défaut les flottants sont écrits en
notation scientifique. Si la mantisse est trop grande, le format est partiellement ignoré.
Inclure des bibliothèques
En Ada, les entrées/sorties ne sont pas intégrées au langage : il est nécessaire d ’inclure les bibliothèques spécifiques qui contiennent ces instructions optionnelles. Text_io : saisie et sortie de caractères Integer_text_io : d ’entiers Float_text_io : de flottants
Note : d'autres constructions peuvent nécessiter d'inclure d'autres bibliothèques...
Inclusion de biblis : with bibli_1, bibli_2; les instructions contenues dans la bibli sont
accessible en les préfixant du nom de la bibli:
with text_io;procedure aaa isbegin
text_io.put('A');end aaa;
Si on utilise fréquemment une instruction en provenance d'une bibli, le préfixage devient vite fatiguant, on le supprime avec "use" :
with text_io;use text_io;…put('A');…
Pourquoi devoir préfixer par défaut ?Utile si 2 biblis apportent des instructions
de même nom
bibli1
mon_put
bibli2
mon_put
programme
with bibli1, bibli2;...mon_put(123);...
lequel est-ce ?
Conversions de types
AP et Ada sont fortement typés : on ne peut pas mélanger des données de types différents dans une même expression.
Il est nécessaire de pouvoir convertir des données d ’un type dans un autre : conversion de type (ou changement de type ou transtypage).
conversion_de_type ::= nom_de_type ( donnée | variable )
nb := integer(3.5); -- nb vaut 3
Structures de contrôle du flux d'instructions
Il est souvent nécessaire que l'état d'un programme (contenu des variables, …) puisse influer sur l'ordre d'exécution des intructions.
C'est le rôle des structures de contrôle de préciser comment et à quelles conditions l'ordre d'exécution des instructions peut varier.
Structure d'alternative AP
Les instructions du bloc "si" ne sont exécutées que si la condition est vérifiée.
Les instructions du bloc "sinon" sont exécutées seulement dans le cas contraire.
si
conditionbloc "sinon"
instructions
bloc "si"
instructions
Structure d'alternative ADA
if condition thenbloc instr.
else -- facultatifbloc instr.
end if;
if condition_1 thenbloc instr.
elsif condition_2 thenbloc instr.
elsif condition_3 thenbloc instr.
...else -- facultatif
bloc instr.end if;
Expressions booléennes, opérateurs relationnels
Une condition est une expression booléenne.
Elle est vérifiée si elle vaut vrai (true).Opérateurs relationnels :
<, <=, >, >=, =, /= et (and), ou (or), non (not) ou exclusif (xor) et alors (and then), ou sinon (or else) dans (in), dehors (not in)
Structure de cas AP
cas : expression
expr_1
bloc_1
instructions
expr_2
bloc_2
instructions
expr_n
bloc_n
instructions
... défaut
bloc_défaut
instructions
La valeur de expression doit correspondre à une et une seule des valeurs données dans les losanges.
Seul le bloc situé sous la bonne valeur est exécuté.
Après exécution de ce bloc, on sort de la structure de cas, les autres blocs sont ignorés.
Structure de cas ADA
case expr iswhen \ expr | interv_discr | others \ {| …} =>
statement {…}{…}
end case; Exemple :case nb is -- nb est une variable entiere
when 1 | 2 | 3 => put("1,2 ou 3); new-line;when 5..10 => put("entre 5 et 10"); new_line;when others => null; -- instruction vide
end case;
3 règles à retenir pour l'ensemble des choix : il doit être exhaustif (d'où l'utilisation
fréquente de others) il ne doit pas y avoir de répétitions les choix doivent être "statiques" (c'est
à dire évaluables lors de la compilation)
Expression statique ou dynamique
Une expression "statique" est une expression dont la valeur peut être calculée lors de la compilation du programme.
Une expression "dynamique" dépend, à priori, de l'exécution du programme.
Ex : le contenu d'une variable est susceptible de changer lors de l'exécution d'un programme (par définition…). Par conséquent, toute expression qui comporte une variable est considérée comme dynamique, même si l'auteur du programme sait que le contenu de la variable en question ne sera pas changé !
Phases d'écriture d'un programmeÉcriture du sourceCompilation : traduction du source en objet
Préprocesseur (nettoie le source) Compilateur (traduit en assembleur) Assembleur (traduit en code machine)
Éditeur de liens : traduction de l'objet en exécutable, regroupement en un seul fichier des différents modules qui constituent le programme
Chargeur : installe l'exécutable en mémoire, puis l'exécute
Structure de boucle AP
Le bloc d ’instruction est exécuté tant que la condition est vraie.
tantque
conditionnom_bloc
instructions
Structure de boucle ADA
while condition loopbloc_instr
end loop;
Exemple :while (nb < 12) loop
put(nb);nb := nb + 1;
end loop;
Boucle « pour » AP
Le bloc d ’instruction est exécuté autant de fois que d'éléments dans l ’intervalle discret [a..b].
La boucle "pour" n'est à utiliser que si on connait à l'avance le nombre de tours à effectuer.
pour
variable dansenvers a..b
nom_bloc
instructionsintervallefacultatif
variable deboucle
La variable de boucle : Ne doit pas être déclarée. Masque toute autre variable déjà déclarée de même
nom, dans tout le sous-arbre du bloc d'instruction. Contient la valeur courante dans l ’intervalle. Est considérée comme une constante dans le bloc
d ’instruction : on ne peut pas la modifier.
Attention : Si b < a, l ’intervalle est vide, on ne le parcourt pas ! Si on souhaite parcourir l ’intervalle à l ’envers,
utiliser le mot-clé « envers ».
Boucle « pour » ADA
for variable in reverse intervalle loopbloc_instr
end loop;
for i in character'('A')..character'('Z') loopput(i);
end loop;
for i in 1..10 loopput(i);
end loop;
Exemples :
-- n ’affiche rien ! for i in 10..1 loop
put(i);end loop;
-- affiche de 10 à 1for i in reverse 1..10
loopput(i);
end loop;
-- affiche 1 à 10, puis 3 !i := 3;for i in 1..10 loop
put(i);end loop;put(i);
-- ne compile pas !for i in 1..10 loop
i := i + 1;end loop;
Boucle avec sortie
loopinstr {…}exit when condition; -- facultatifinstr {…}
end loop;
Exemple :-- compte une séquence de nombres terminée par 0-- nb et compteur sont des variables entièresput("Entrez vos nombres, terminez par 0"); new_line;loop
get(nb);exit when nb = 0;compteur := compteur + 1;
end loop;put("Decompte = "); put (compteur); new_line;
De plus, les boucles ADA peuvent recevoir un identifiant de boucle, qui peut se combiner avec l'instruction exit.
Bcl1: loop{…}
Bcl2: loop {…} exit Bcl1;
end loop;end loop;
Résumé des différentes structures de boucle en ADA
[id:] [while bool_expr | for id in [reverse] discrete_range] loopstatement {…}
end loop [id];
exit [loop_id] [when bool_expr];
Bloc déclaratif AP
Il peut être utile de déclarer des variables pour un usage momentané : leur portée peut être réduite au bloc d'instruction où elles servent.
En AP : nom_blocvar1, var2 : type1;var3, var4, var5 : type2;
bloc_instr
Bloc déclaratif ADA
[id:] declaredeclarative_item
beginhandled_statement
end [id];handled_statement ::=
statement {…}[exception_handler]
Le "handled_statement" est un simple bloc d'instructions, qui se termine par un traitant d'exceptions : des instructions servant à rattraper d'éventuelles erreurs lors de l'exécution.
Exemple de bloc déclaratif :declare
tmp : integer;begin
tmp := A; A := B; B := tmp;end;
Types tableaux
De nombreuses applications nécessitent de mémoriser des séries de variables : composantes de vecteurs ou de matrices, tables de température ou de pression, …
On peut stocker ces données dans une structure simple appelée tableau, à l'intérieur de laquelle chaque donnée est associée à un ou plusieurs numéros appelé indices.
On accède à une donnée en rappelant le nom du tableau et son ou ses indices.
Tableaux AP
Représentation d'un tableau à 1 dimension :
temperatures 16 14 14 13 16 14 12 12
1 2 3 4 5 6 7 8
nom du tableau
indices
variablestemperatures(1) temperatures(6)
Une variable de type tableau doit être déclarée, en précisant le type unique des valeurs contenues dans le tableau et les bornes du tableau :temperatures : tableau (1..8) d'entiers;
Tableaux ADA
On peut les déclarer comme des variables :tab1 : array(1..8) of integer;tab2 : array(1..8) of integer;
Attention, tab1 et tab2 sont considérés comme étant de types différents : pas d'affectation possible au niveau tableau
sans une conversion. pas de possibilité de les passer en paramètre à
une fonction ou à une procédure (voir plus loin).
En général, on déclare un type tableau avant de déclarer les variables :type tab_int_8 is array(1..8) of integer;tab1, tab2 : tab_int_8;
Pas de problème d'affectation, ni de passage en paramétre.
Les déclarations de types se placent dans la partie déclarative d'une unité de compilation ou d'un bloc déclaratif.
Nous utiliserons la même convention en AP.
Exemple sur les tableauxmax_tabmax : entiertype tab20int est tableau(1..20) d'entierstab : tab20int;
saisie déterminer_max afficher_max
i dans2..taille
i dans1..taille
pour seq
pour
seq
Comparer avec tousinitialiserEntrer(tab(i))
max := tab(1)
Sortir(max)
max := tab(i)max < tab(i)
si
Procedure max_tab isTaille : constant := 20;type Tab20Int is array(1..Taille)
of integers;max : integer;tab : Tab20Int;
begin-- saisieput_line("entrez le tableau");for i in 1..Taille loop
get(tab(i));end loop;-- determiner max-- initialisermax := tab(1);
-- comparer avec tousfor i in 2..Taille loop
if max < tab(i) thenmax := tab(i);
end if;end loop;-- afficher maxput("le max est :");put(max);new_line;
end max_tab;
Règles, opérations et attributs de tableaux
un tableau ne peut contenir qu'un seul type d'éléments;
on ne peut pas utiliser un indice en dehors des bornes définies à la déclaration du tableau, sous peine de "plantage";
on peut affecter un tableau à un autre tableau s'ils sont compatibles : même taille, même type d'éléments (une conversion de type peut être nécessaire) :
a, b : tab_int;
a := b;
Un tableau connaît ses bornes : a'first, a'last l'intervalle des indices corrects : a'range (équivalent
à a'first..a'last) la longueur de cet intervalle : a'length
On peut comparer des tableaux (<, >, <=, >=) selon le principe de l'ordre lexicographique quand tout est égal, un tableau plus court est
considéré comme plus "petit" ou "avant" dans l'ordre.
On peut concaténer des tableaux avec &a(1..10) := b(1..5) & c(2..6);
Les tranches
On peut extraire un "morceau" de tableau, il suffit que les indices soient contigus :a, b : tab_int;a(1..5) := b(6..10);
Attention lors d'une telle affectation à vérifier que les 2 tranches soient de même taille.
Les tranches peuvent se superposer :a(1..5) := a(2..6) -- décale la tranche 2..6 d'une
case
Les agrégats
Permettent d'initialiser les tableaux :declare
type TABLE is array(4..9) of integer;t : TABLE := (1, 2, 3, 4, 5, 6);
begint := TABLE'(4 | 6 => 1, others => 0);t := TABLE'(4..7 => 2, 8..9 => 3);
end;
Les chaînes de caractères APLes chaînes de caractères sont des données
très utilisées : il existe un type chaine prédéfini.
Le type chaine est un en fait un tableau de caractère, avec une syntaxe raccourcie :ch1, ch2 : chaine(1..8); -- conseilléch3 : tableau(1..8) de caractères; -- déconseillé
Attention, dans l'exemple précédent ch1 et ch2 sont de même type, mais pas ch3, bien que ces 3 objets soient représentés de manière identique en mémoire.
Les chaînes de caractères ADAEn ADA, on dispose du type STRING :
ch1, ch2 : string(1..8); -- conseilléch3 : array(1..8) of character; -- déconseillé
Comme en AP, ch1 et ch2 sont de même type, mais pas ch3, bien que ces 3 objets soient représentés de manière identique en mémoire.
Accès :ch1(1) := ch2(8);ch1(1..5) := ch2(1..2) & ch2(2..4);
Entrées/sorties sur les chaînes
Entrée :get(ch1); -- attention, taper exatement 8 caractères!!!get_line(ch1, lng1); -- le nombre de cars tapés sera
-- rangé dans lng1, qu'il faut déclarer (var. entière).
Sortie :put(ch1);put_line(ch1); -- passe à la ligne suivante
-- !!! Put_line ne fonctionne qu'avec des chaînes !!!
Attributs relatifs aux chaînes et aux caractères
Image : renvoie la représentation d'un type sous forme de chaîne.
Value : renvoie la "valeur" d'une chaine.Pred, Succ : le précédent et le suivant.Pos, Val : la position et son inverse
Character'pred('2') = '1'Character'succ('A') = 'B'Character'pos('0') = 48Character'val(48) = '0'
Integer'image(300) = "300"Character'image('z') = "z"Integer'value("300") = 300Character'value(" 'z' ") = 'z'
Les tableaux à plusieurs dimensions
Array_type_def ::=array (discrete_range {, …}) of type
Exemple :type MATRICE is array(1..10, 1..10, 1..10)
of float;type TAB_2D is array(1..5, -1..0) of integer;m1, m2 : MATRICE;t : TAB_2D;
Accès : m1(1,1,1) := m2(1,1,10); get(m1(i,j,k));
Agrégats : t := TAB_2D'((1,1), (2,2), (3,4), (4,5), (5,5)); t := TAB_2D'((1,1), (2,2), others => (3,3)); t := TAB_2D'(1 | 3 => (1,1), others =>
(0,0 ); t := TAB_2D'(others => (-1 => 1, others =>
0)); t := TAB_2D'(others => (others => 0));
Attributs : first(i), last(i), range(i), length(i) où i est
le numéro de la dimension :t'first(1) = 5t'first(2) = -1t'range(1) = 1..5t'range(2) = -1..0
first(1), last(1), range(1), length(1) se notent aussi first, last, range, length.
Les sous-programmes : procédures et fonctionsSubprogram_body ::=
subprogram_spec is{declarative_item}
beginhandled_statement
end;Subprogram_spec ::=
procedure [parent_name.]id [formals]| function [parent_name.]id [formals]
return subtype_name
Formals ::=(id {, id} : [in | in out | out] subtype_name
[:= expr] {; …})Exemples :
procedure efface_ecran is …procedure passe_lignes (x : integer) is …function lire_entier return integer is …function perimetre (rayon : float) return float is …procedure peri_surf (rayon : in float; perimetre,
surface : out float) is ...
procedure exemple is……begin
…passe_lignes(2);…N := 10;passe_lignes(N);…
end exemple;
procedure passe_lignes(X : integer) isbegin
for cpt in 1..X loopnew_line;
end loop;end passe_lignes;
Flux des instructions
Appels de sous-programme
Paramètre effectif
Paramètre formel
Un sous-programme est exécuté à chaque appelLors de l'appel, le paramètre formel prend la valeur du paramètre effectif
Spécification de sous-programme
Les sous-programmes servent : à éviter les répétitions inutiles de code, lorsqu'un
ensemble d'instructions sert à plusieurs endroits. à structurer le programme en isolant des
fonctionnalités spécifiques dans des sous-programmes séparés : c'est comme si on ajoutait de nouvelles instructions de base.
Les sous-programmes doivent : éviter au maximum d'utiliser des variables déclarées
en dehors d'eux-mêmes : utiliser les paramètres pour communiquer les données.
conserver une taille raisonnable pour pouvoir être compris et mis au point sans pb : au maximum 60 lignes
Position des procédures et fonctions1) Dans des fichiers séparés, un par
procédure/fonction : On les inclus dans le programme principal avec
"with nom_proc" (le fichier doit avoir le même nom).
La clause "use" n'est pas nécessaire. Elles ne connaissent pas les types/variables du
programme principal !
2) Dans la procédure principale, dans la section déclarative (là où on place les déclarations de types, de variables, …) Elles connaissent les déclarations placées avant.
Déclarations préliminairesUne procédure ne peut en appeler une autre
qui soit déclarée après : C'est génant, obligation de bouger le morceau
de code à la bonne place. C'est parfois inextricable : si p1 appelle p2 et p2
appelle p1, bouger le code n'est pas la solution.
On recourt à une déclaration préliminaire, que l'on place le plus haut possible : Même syntaxe qu'une déclaration ordinaire, sauf
qu'on s'arrête au "is", qui est remplacé par ";" procedure lire_somme (s : out integer) ; -- decl.
prelim.
Paramètres et valeur de retourLes paramètres des sous-programmes et la
valeur de retour des fonctions servent à échanger des données avec le programme appelant. dans la déclaration du ss-prog : paramètres formels dans l'appel au ss-prog : paramètres effectifs
Une procédure ne retourne pas de valeur, mais elle peut prendre des paramètres qui peuvent être modifiés.
Une fonction retourne obligatoirement une unique valeur. Ses paramètres ne peuvent être modifiés (penser aux fonctions mathématiques).
La valeur d'une fonction est retournée par l'instruction : return expression;
Si le flux d'instruction atteint le "end" final d'une fonction sans rencontrer de "return", l'exception "program_error" est levée.
On peut utiliser return ; dans une procédure : on quitte la procédure en cours et on revient à l'appelant (rien n'est retourné).
Les modes des paramètres : in : lecture seule (comme une constante) out : écriture seule in out : lecture / écriture (comme une variable)
Si on n'indique pas de mode, c'est le mode "in" par défaut.
Une fonction a toujours ses paramètres en mode "in".
Attention, en mode "out" on ne peut pas consulter (lire) la valeur du paramètre, ce qui peut être déconcertant.
Procedure lire_somme (s : out integer) is-- met dans s la somme des entiers saisis par l'utilisateur
n, tmp : integer := 0;
beginput_line("entrer vos entiers (terminer par -1)");get(n);while n /= -1 loop
-- s := s + tmp; est interdit (car mode "out")tmp := tmp + n; get(n);
end loop;s := tmp; -- on a seulement le droit d'ecrire dans s
end;
Paramètres par défaut
Les paramètres peuvent recevoir une valeur par défaut : on n'est alors pas obligé de les fournir.
Dans text_io, "new_line" est déclaré ainsi :procedure new_line (spacing : positive := 1) is …
On peut appeler new_line sans paramètre, dans ce cas new_line passe 1 seule ligne.
Portée d'une entité (type, variable, constante…) : partie du code où sa déclaration est effective, c'est à dire où l'entité existe.
Visibilité d'une entité : partie du code où l'entité est accessible.
La visibilité est toujours incluse dans la portée : on ne peut accéder qu'à une entité qui existe.
La visibilité est souvent plus réduite que la portée : masquages temporaires par des entités portant le même nom et plus locales.
Portée et visibilité
Toute entité déclarée dans la partie déclarative d'un sous-programme ou d'un bloc "declare" : n'est visible que dans ce sous-
programme ou ce bloc. disparaît une fois le sous-programme ou
le bloc terminé.
With … ; use … ;Procedure portee_visibilite is
A : constant := 123;B : integer := A + 1; -- debut de la visibilite de AC : integer := 12; -- debut de la visibilite de Bfunction f (B : character := integer'image(C)(2)) -- debut visibilite C
return integer isC : integer := 0; -- debut de la visibilite de B de f
begin -- debut de la visibilite de C de freturn character'pos(B) + C;
end;begin -- retour de la visibilite de B et C après masquage dans f
put(A); put(f('A')); put(f); put (B); put(C); put(integer'image(C));-- affiche : 123 65 49 124 12 12
end portee_visibilite;
Récursivité
Un sous-programme peut s'appeler lui-même :function factorielle(N : integer) return integer isbegin
if N = 0 thenreturn 1; -- condition d'arrêt
elsereturn N * factorielle(N - 1); -- appel
récursifend if;
end factorielle;
CE QU'IL FAUT COMPRENDRE :
chaque niveau d'appel récursif crée sa propre copie des paramètres formels et des variables locales.
lors du retour d'un appel récursif on retrouve donc intactes les valeurs des paramètres et des variables (sauf si elles ont été passées récursivement en mode "out", bien entendu !)
dans le code du sous-programme, il doit y avoir une alternative où on ne relance pas un appel révursif, sinon la récursion est infinie (concrètement ça plante après avoir saturé la mémoire de la machine).
Plusieurs sous-programmes peuvent s'appeler les uns les autres :
-- conjecture de syracuseget(N);put(syracuse1(N));
function syracuse2(N : integer) return integer isbegin
if N /= 1 and N % 2 /= 0 then return syracuse2(3 * N + 1);
elsereturn syracuse1(N);
end if;end;
function syracuse1(N : integer) return integer isbegin
if N /= 1 and N % 2 = 0 then return syracuse1(N / 2);
elsif N /= 1 thenreturn syracuse2(N);
elsereturn N;
end if;end;
Attention, dans l'exemple précédent on a besoin de déclarations préliminaires pour compiler.
Comment aborder un problème récursif ? Il faut avoir l'intuition qu'il se traite
récursivement. Si on ne sait pas le traiter de manière classique, ça ne coûte rien de se poser la question…
Il faut déterminer sur quelle "variable" (ex: N) on fait la récurrence.
Il faut avoir une solution d'arrêt de la récursivité (quand N est petit).
Supposant avoir la solution au rang N-1, trouver ce qu'il faut faire pour passer au rang N.
Retour sur les types
Type / Sous-type une déclaration de type est statique
type mon_entier is range 1..100; -- okN : integer := 100; -- variable, non statique type mon_entier2 is range 1..N; -- interdit !!!
une déclaration de sous-type peut être dynamique, elle s ’appuie sur un type pré-existantN : integer := 100; -- variablesubtype mon_entier3 is integer range 1..N;
Types entierswith ada.text_io; use ada.text_io;
procedure test_instanciation is type mon_entier is range 1..100; package mon_entier_io is new ada.text_io.integer_io(mon_entier);
use mon_entier_io; -- autorise les E/S sur ce nouveau type
n : mon_entier; begin put("entrer un entier : "); get(n); -- raise DATA_ERROR si pas dans 1..100 put(n * 2); -- raise CONSTRAINT_ERROR si pas dans 1..100
end;
Types énumératifswith ada.text_io, ada.integer_text_io;use ada.text_io, ada.integer_text_io;procedure test_type_enumere is type JOUR is (LUNDI, MARDI, MERCREDI, JEUDI, VENDREDI, SAMEDI, DIMANCHE);
package jour_io is new ada.text_io.enumeration_io(jour);
use jour_io; n : integer;begin put("entrer un entier : "); get(n); put(JOUR'VAL(n mod 7));end;
Attributs pour types énumérés jour'first # LUNDI jour'last # DIMANCHE jour'succ(LUNDI) # MARDI jour'pred(MARDI) # LUNDI jour'pos(LUNDI) # 0 jour'val(0) # LUNDI jour'value("LUNDI") # LUNDI jour'image(LUNDI) # "LUNDI"
Type booléen type boolean is (FALSE, TRUE); -- type énuméré
Types flottants On peut préciser le nombre de chiffres
significatifs : digits N = précision au 1/10^N On peut limiter l'intervalle des valeurs possibles
type REEL is digits 6; type VOLTAGE is digits 5 range 0.0 .. 1.0E+9;
Attributs type FL is digits D range I..SFL'digits # D FL'first # I FL'last # S
E/S : instancier ada.text_io.float_io(nom_type)
Types tableaux contraints / non contraints tableaux contraints
type vecteur1 is array(1..10) of float;v1, v2 : vecteur1;toutes les variables de type vecteur1 ont
mêmes dimensions
tableaux non contraintstype vecteur2 is array(integer range <>) of float;v1 : vecteur2(1..10);v2 : vecteur2(-5..15);v1 et v2 sont de même type, mais de
dimensions différenteson doit fournir les bornes lors d'une déclaration
de variable de type vecteur2
Intérêt des tableaux non contraints
Quand ils apparaissent en tant que paramètres formels, il n'est pas nécessaire de préciser leur taille (car ce n'est pas une déclaration de variable) :
Procedure truc(v : in out vecteur2) is …On utilise les attributs de tableau pour
connaître l'intervalle des indices.
Rappel : les chaînes de caractèresEn fait le type STRING est simplement un type
tableau de caractères non contraint :type STRING is array(integer range <>) of
character;
Type Article / Enregistrement (record)
Si un tableau est une collection de variables de même type accessibles via leur numéro, un article est une collection de variables de types qui peuvent être différents, accessibles par leur nom.
Les différentes variables de l'article s'appellent des champs.
La variable de type article possède son propre nom, on accède à ses champs par la notation pointée :
nom_d_article.nom_de_champ
Exemple :type MOIS is (Janvier, Fevrier, …, Decembre);type DATE is record
jour : integer range 1..31;mois : MOIS;an : integer;
end record;
d : DATE;d := (14, Juillet, 1789);d := (jour => 14, an => 1789, mois => Juillet);d.mois := Mars; d.jour := 12; d.an := d.an + 1;
Type Article avec partie variante type Genre_Figure is (un_cercle, un_point,
un_carre); type Point is record X, Y : Integer; end record; type Figure (Sorte : Genre_Figure := un_point) is
record Position : Point; case Sorte is when un_cercle => Rayon : Integer; when un_carre => Coin_Inf_Droit : Point; when others => null; end case; end record;
F : Figure := (un_point, (10,15)); F := (un_cercle, position => (20, 25), rayon => 50);
Partie variante : comment ça marche ?
Entre les différents contructions rendues possibles dans la partie variante, le compilateur calcule celle qui occupe le plus de place et alloue cette place à la variable.
Dans l'exemple, c'est le carré qui prend le plus de place. Si on charge la variable avec un point ou un cercle, une partie de la mémoire reste inutilisée, tout simplement.
On ne peut changer le discriminant (variable qui sélectionne la partie variante) que si on change tout le reste de l'article.