Programmation Pascal/Version imprimable
Une version à jour et éditable de ce livre est disponible sur Wikilivres,
une bibliothèque de livres pédagogiques, à l'URL :
https://fr.wikibooks.org/wiki/Programmation_Pascal
Introduction
Le langage de programmation Pascal (dont le nom vient du mathématicien français Blaise Pascal) a été inventé par Niklaus Wirth dans les années 1970. Il a été conçu pour servir à l'enseignement de la programmation de manière rigoureuse mais simple, en réaction à la complexité de l'ALGOL 68. Le Pascal est donc principalement utilisé dans l'enseignement et notamment dans les universités.
Ce livre explique comment implémenter un algorithme impératif en Pascal pour ensuite l'exécuter sur une machine. Ce livre n'enseigne pas comment créer des algorithmes et résoudre des problèmes mais comment, à partir d'un algorithme impératif créer un programme informatique.
Prérequis cognitif :
- Algorithmique impérative, nécessaire pour créer des algorithmes avant de les implémenter.
- Utilisation simple de l'ordinateur (édition de texte, shell simple)
Prérequis matériels :
- un ordinateur, n'importe lequel et de n'importe quelle architecture du moment que les prérequis logiciels sont respectés.
Prérequis logiciels :
- un éditeur de texte : le plus simple suffit. Un Environnement de développement pascal serait un plus.
- un compilateur Pascal fonctionnant pour cet ordinateur.
Exemple d'environnement de travail classique :
- Lazarus (créé en... Pascal) pour éditer le code source
- Free Pascal comme compilateur
- Le tout sous le système d'exploitation Linux, Microsoft Windows, ou Mac OS
- Sur un ordinateur de type PC ou Macintosh.
Environnement de développement
Un simple éditeur de texte et un compilateur Pascal en ligne de commande peuvent suffire à un usage simple. Cependant l'utilisation d'un environnement de développement intégré facilite les tâches, notamment le débogage des programmes. Il existe aussi des compilateurs spécifiques pour des applications embarquées (sur téléphone mobile par exemple).
Lazarus et Free Pascal
[modifier | modifier le wikicode]
Free Pascal est un compilateur multiplateformes (PC, Mac, Téléphone mobile, Consoles...) pour divers processeurs (x86, ARM, PowerPC...) en 32 et 64 bits. Lazarus est un environnement de développement intégré pour la création rapide d'application (RAD : Rapid Application Development).
|
MIDletPascal
[modifier | modifier le wikicode]
MIDletPascal est un environnement de développement intégré et un compilateur fait pour la création d'applications sur téléphones mobiles. Le compilateur génère du bytecode Java pour JavaME, qui peut tourner sur différents appareils. |
Un premier programme
Le fichier source
[modifier | modifier le wikicode]Le classique Hello world en Pascal, enregistrez le code suivant dans un fichier texte nommé hworld.pas
:
program hworld;
begin
writeln('Hello world!')
end.
Compilez ce programme, et lancez-le. Le message suivant doit normalement s'afficher :
Hello world!
Le code source en détail
[modifier | modifier le wikicode]program hworld;
Le code source commence par déclarer qu'il s'agit d'un programme nommé hworld
Remarque:
Le nom du programme pourrait être Hello_World et comporter ici 11 caractères par exemple, même sous DOS : la chaine de caractères n'est pas trop longue car il ne s'agit pas là du nom du fichier DOS en format 8.3, mais simplement du nom du programme pour le compilateur.
Par contre, le fichier quant à lui devra ne comporter que 8 caractères, par exemple hworld.pas.
begin
Début de la routine principale, appelée au démarrage du programme.
writeln('Hello world!')
Appel à la procédure writeln
(write line) pour afficher la chaîne Hello world!
sur la sortie standard.
end.
Fin de la routine principale et du programme.
Donc,
program Hello_World;
begin
writeln('Hello world!')
end.
se compile et s'exécute très bien avec n'importe quel compilateur Pascal, y compris les anciens depuis Turbo Pascal 3 sous DOS.
Structure d'un programme
Instructions et blocs
[modifier | modifier le wikicode]Une instruction séquence d'instructions regroupe plusieurs instructions en une seule.
Celle-ci commence par le mot clé begin
et se termine par end
.
Le ; sert à séparer deux instructions.
Voici un exemple :
begin
temp := x ;
x := y ;
y := temp
end;
Cette instruction Pascal sera traduite par un bloc en Java
{
int temp = x ;
x = y ;
y = temp ;
}
Catégorie
[modifier | modifier le wikicode]Un fichier source Pascal débute toujours par un mot clé indiquant le type de code :
- Un programme, c'est à dire une application indépendante (
program
) ; - Un module utilisable dans un programme ou un autre module (
unit
).
Ce mot clé doit être suivi d'un identifiant (un nom) afin de pouvoir faire référence à ce module ou ce programme.
Afin que le compilateur trouve les modules à partir de leur nom, il faut que le nom du fichier source soit identique : un module nommé par :
unit my_unit;
doit être enregistré dans un fichier nommé my_unit.pas
.
Exemple 1 : un programme
[modifier | modifier le wikicode] program first_app;
...
Exemple 2 : un module
[modifier | modifier le wikicode] unit my_unit;
...
Exemple 3 : un programme utilisant le module de l'exemple 2
[modifier | modifier le wikicode]Le mot clé uses
spécifie la liste des modules utilisés :
program second_app;
uses my_unit;
...
Compilation
[modifier | modifier le wikicode]L'extension des fichiers produits par la compilation varie selon la plateforme utilisée et le compilateur (natif ou P-code).
Cependant, la plupart des compilateurs ne gère pas la compilation en chaîne : chaque module et programme doit être compilé séparemment et deux modules ne peuvent donc s'utiliser mutuellement.
Commentaires
[modifier | modifier le wikicode]Les commentaires sont encadrés par les accolades :
program first_app;
{ ma première application en Pascal }
...
Alternativement, on peut utiliser les parenthèses-étoiles :
program first_app;
(* ma première application en Pascal *)
...
Ce qui était plus pratique avec les anciens claviers ne comportant pas les accolades.
Le compilateur Turbo Pascal utilise certains commentaires spéciaux pour préciser des directives de compilations. Ceux-ci débutent par un caractère $, par exemple {$R-,I-}
pour désactiver la vérification des erreurs d'intervalle (Range errors) et des erreurs d'entrée-sortie (I/O errors).
Structure générale d'un programme
[modifier | modifier le wikicode] program {nom du programme};
uses {liste des modules utilisés};
{...déclaration des variables globales et des types...}
{...déclaration des procédures et fonctions...}
begin
{...instructions de la routine principale...}
end.
Structure générale d'un module
[modifier | modifier le wikicode]Le module est divisé en deux grandes parties :
- La partie
interface
expose les fonctions, procédures, variables et types publiques, c'est à dire accessibles depuis les modules et programmes qui utilisent ce module. - La partie
implementation
contient le code implémentant les fonctions et procédures publiques, et peut contenir d'autres variables, types, fonctions et procédures internes au module.
unit {nom du module};
interface
uses {liste des modules utilisés};
{...déclaration des variables globales et des types...}
{...déclaration des procédures et fonctions...}
implementation
uses {liste des modules utilisés};
{...déclaration des variables globales et des types...}
{...déclaration des procédures et fonctions...}
begin
{...instructions d'initialisation du module...}
end.
Variables
Une variable sert à stocker une donnée. Elle a un nom, et un type (entier, chaîne de caractères, tableaux d'entiers, ...).
Déclaration des variables
[modifier | modifier le wikicode]Les variables peuvent être déclarées en début de programme ou de module. Dans ce cas, elles sont globales, car accessibles à toutes les procédures et fonctions qui suivent.
Les variables peuvent également être déclarées en début de procédure ou de fonction, où elles seront locales à celle-ci.
Les déclarations de variables commencent par le mot clé var
, puis chaque variable est déclarée selon la syntaxe suivante :
nom_variable : type_de_variable ;
Il est également possible de regrouper les variables ayant le même type :
nom_variable1 , nom_variable2 : type_de_variable ;
Par exemple :
var
i,j : integer;
s : String;
Affectation
[modifier | modifier le wikicode]Pour affecter une valeur constante ou le résultat d'une expression à une variable, il faut utiliser l'opérateur :=
(caractère deux-points suivi du caractère égal).
Par exemple :
i:=5;
j:=i+10;
Affectation à la déclaration
[modifier | modifier le wikicode]Il est possible d'affecter une valeur à la déclaration d'une variable, en utilisant l'opérateur égal ( =
), le caractère deux-points étant déjà utilisé pour déclarer le type :
var i:integer=5;
Procédures et fonctions
Une procédure ou une fonction est une sous-routine, en général appelée plusieurs fois. Ceci permet de regrouper un code utilisé plusieurs fois, de diviser le programme en sous-programmes.
Différences procédure/fonction
[modifier | modifier le wikicode]Procédures et fonctions acceptent des paramètres, seule une fonction retourne une valeur. La procédure n'en retourne pas.
Déclaration d'une procédure
[modifier | modifier le wikicode]La déclaration d'une procédure se fait avec le mot clé procedure
suivi du nom de la procédure, et de la liste des paramètres entre parenthèses. Cette liste de paramètres est déclarée avec la même syntaxe que les variables.
Ensuite, les variables locales sont déclarées, puis les instructions sont comprises dans un bloc begin
... end;
Par exemple :
procedure NumeroFoisDix( numero : integer ) ;
var j : integer;
begin
j := numero * 10 ;
writeln('j = ', j) ;
end ;
Déclaration d'une fonction
[modifier | modifier le wikicode]La syntaxe est similaire à celle d'une procédure, excepté que le mot clé utilisé est function
et que le type de retour doit être déclaré.
Par exemple :
function NumeroPlusCinq( numero : integer ) : integer ;
var j : integer ;
begin
j := numero + 5 ;
NumeroPlusCinq := j ;
end ;
Retourner une valeur se fait en affectant celle-ci au nom de la fonction.
Interface et implémentation dans un module
[modifier | modifier le wikicode]Dans un module, la partie implementation
contient le code des sous-routines en utilisant la syntaxe des paragraphes précédant.
La partie interface
ne reprend que la première ligne.
Par exemple :
unit MonModule;
interface
procedure NumeroFoisDix( numero : integer ) ;
function NumeroPlusCinq( numero : integer ) : integer ;
implementation
procedure NumeroFoisDix( numero : integer ) ;
var j : integer ;
begin
j := numero * 10 ;
writeln("j = ", j) ;
end ;
function NumeroPlusCinq( numero : integer ) : integer ;
var j : integer ;
begin
j := numero + 5 ;
NumeroPlusCinq := j ;
end ;
end.
Ordre de déclaration
[modifier | modifier le wikicode]La compilation en une passe implique que chaque fonction ou procédure appelée doit être définie avant. L'ordre des sous-routines est donc important.
L'exemple suivant produit une erreur de compilation :
procedure FaireFaire;
begin
writeln('Procédure qui sous-traite l''action');
Faire;
end;
procedure Faire;
begin
writeln('Procédure qui fait l''action');
end;
L'ordre correct est le suivant :
procedure Faire;
begin
writeln('Procédure qui fait l''action');
end;
procedure FaireFaire;
begin
writeln('Procédure qui sous-traite l''action');
Faire;
end;
Cette restriction ne s'applique pas aux sous-routines publiques d'un même module, car elles sont pré-déclarées dans la partie interface.
Il existe des cas où deux procédures s'appellent mutuellement. Aucune ne peut être située avant/après l'autre. Dans ce cas, il faut en prédéclarer une avec le mot clé forward
.
Par exemple :
procedure GrandCalcul(n:integer); forward;
procedure PetitCalcul(n:integer);
begin
if n>100 then GrandCalcul(n);
else begin
...
endif;
end;
procedure GrandCalcul(n:integer);
begin
if n<=100 then PetitCalcul(n);
else begin
...
endif;
end;
Dépendances mutuelles et références circulaires
Dépendances mutuelles et références circulaires
[modifier | modifier le wikicode]Parfois, on est amené à faire deux unités où chacune fait références aux fonctions de l'autre. Il s'agit d'une dépendance mutuelle. Dans certains cas, cela peut poser des difficultés.
Dépendance au niveau implémentation
[modifier | modifier le wikicode]Pour commencer, il y a un cas où cela marche sans problème, c'est quand la clause uses qui fait référence à l'autre unité se trouve dans la section d'implémentation. Dans ce cas, l'ensemble des déclarations de types, de procédures et de fonctions sont lues dans chacune des deux unités. En effet, ces déclaration se trouve dans la section interface, c'est-à-dire avant que la question de la dépendance mutuelle soit soulevée. Quand, dans l'implémentation, on fait référence à l'autre unité, tous les membres publics sont déjà définis.
Dépendance mixte interface-implémentation
[modifier | modifier le wikicode]Il se peut qu'une unité A fasse référence à une unité B par une cause uses dans sa partie implémentation, tandis que l'unité B fasse référence l'unité A dans sa partie interface. Dans ce cas, le compilateur va d'abord lire l'interface de A parce qu'elle est nécessaire à l'interprétation de l'interface de l'unité B. Par exemple, l'unité B peut proposer des fonctions qui prennent en paramètre un type défini dans l'unité A. Une fois l'interface de B déterminée, le compilateur va s'occuper des implémentations. Par la suite, l'unité A et l'unité B peuvent utiliser dans leur implémentation les fonctions de l'autre. Référence circulaire ou dépendance interface-interface
Maintenant, si l'on fait deux unités qui font référence l'une à l'autre, mais avec une clause uses dans leur interface, on obtient l'erreur de référence circulaire. En effet, pour pouvoir interprêter correctement l'interface d'une unité, il faut que le compilateur ait lu l'interface de l'autre unité, et vice-versa. Certains compilateurs ne génère pas d'erreur dans ces cas-là, mais en Pascal, pour le moment, une telle chose est impossible.
Fausse référence circulaire et vraie référence circulaire
[modifier | modifier le wikicode]Il se peut qu'en réalité, il ne soit pas nécessaire de déclarer la dépendance au niveau de l'interface de l'unité. Dans ce cas, il suffit de déplacer une partie de la clause uses dans la partie implémentation, pour les unités ne faisant appel seulement à ce moment à des procédures, des fonctions, ou à des types déclarés dans d'autres unités. C'est le cas le plus fréquent fort heureusement.
Sinon, c'est généralement que certains objets définis dans une unité A contiennent des champs ou des méthodes utilisant des objets définis dans une unité B, qui eux-même contiennent des champs ou des méthodes utilisant des objets définis dans l'unité A. Par exemple :
{ dans l'unité A }
type
TObjetA = class
function DonneObjetB: TObjetB;
end;
{ dans l'unité B }
type
TObjetB = class
function DonneObjetA: TObjetA;
end;
Solution par la fusion
[modifier | modifier le wikicode]On peut régler le problème de la référence circulaire en mettant les deux objets dans une seule et même unité et en prédéclarant les types. Cela se fait de la manière suivante :
{ dans l'unité fusionnée AB }
type
TObjetA = class; { prédéclaration de A }
TObjetB = class; { prédéclaration de B }
TObjetA = class { déclaration complète de A }
function DonneObjetB: TObjetB;
end;
TObjetB = class { déclaration complète de B }
function DonneObjetA: TObjetA;
end;
Bien entendu, il se peut que, de fil en aiguille, on obtiennent des fichiers de code très gros, ce qui est l'inconvénient de cette méthode.
Solution par le non-typage
[modifier | modifier le wikicode]Enfin, on peut résoudre le problème en ne typant pas les champs ou les paramètres et les valeurs de retour des procédures et des fonctions.
{ dans l'unité A }
type
TObjetA = class
function DonneObjetB: TObject;
end;
{ dans l'unité B }
type
TObjetB = class
function DonneObjetA: TObject;
end;
Bien que la solution ne soit pas très élégante, elle permet de contourner le problème. Lors de l'appel des fonctions DonneObjetA et DonneObjetB, il faudra faire un transtypage pour avoir un objet bel et bien identifié comme étant de type TObjetA ou TObjetB. Par exemple :
procedure UtiliseObjetA;
var objA : TObjetA;
begin
objA := TObjetA(objB.DonneObjetA);
objA.Affiche;
end;
Créer de nouveaux types
Déclaration des types
[modifier | modifier le wikicode]Les déclarations de types commencent par le mot clé type
, puis chaque nouveau type est déclaré selon la syntaxe suivante :
nom_type = type ;
Par exemple :
type
entier = integer;
vraifaux = boolean;
Ensuite, les variables peuvent utiliser ces types :
var i:entier;
b:vraifaux;
Structure
[modifier | modifier le wikicode]Une structure est appelée record (enregistrement) en Pascal. Ce type regroupe plusieurs données, chacune portant un nom. Elle ne nécessite pas d'allocation de mémoire particulière, au contraire des objets (type class
).
Exemple
[modifier | modifier le wikicode] type
point = record
x , y : integer ;
end record;
Une variable de ce type :
var point_depart : point;
possède deux membres accessibles par l'opérateur point ( .
) :
point_depart.x := 10;
point_depart.y := 15;
Exercices
Exercice « Fractions égyptiennes »
[modifier | modifier le wikicode](Niveau difficile)
Toute fraction peut s’écrire comme une somme de fractions ayant 1 comme numérateur. Cette décomposition est appelée décomposition en fractions égyptiennes. En voici un exemple : 7/8 = 1/2 + 1/3 + 1/24
Écrire un programme Pascal prenant en entrée le numérateur N et le dénominateur D d’une fraction et affiche sa décomposition en fraction égyptiennes.
Exercice « Nombre premier »
[modifier | modifier le wikicode](Niveau moyen)
Écrire un programme qui demande à l'utilisateur de saisir un entier N et qui affiche le énième nombre premier.
Exercice « Nombres super premiers »
[modifier | modifier le wikicode](Niveau moyen)
Un nombre est dit super premier s'il est premier et si, en supprimant des chiffres à partir de sa droite, le nombre restant est aussi premier.
Exemple : Le nombre 59399 est super premier car les nombres 59399, 5939, 593, 59 et 5 sont tous premiers.
Écrire un programme Pascal qui permet de : Saisir un entier n tel que 40000 < n < 100000, Chercher tous les nombres premiers inférieurs ou égaux à n, les afficher à raison d'un nombre par ligne en mentionnant devant chaque nombre super premier la note « super premier ».
Exercice « Nombres binaires »
[modifier | modifier le wikicode](Niveau facile)
On se propose d’écrire un programme Pascal intitulé conversion permettant de saisir un nombre binaire de longueur minimale 2 puis de le convertir en décimal. Pour ce faire, on multiplie la valeur de chaque bit* 2 puissance «le poids du bits (ordre-1)».
N.B : On rappelle qu’un nombre binaire est composé uniquement par des 0 et des 1.
Exemple : (1001101)2= 1*26+0*25+0*24+1*23+1*22+0*21+1*20 = 64+0+0+8+4+0+1 = (77)10.
Exercice « Jeu des allumettes »
[modifier | modifier le wikicode](Niveau facile)
Écrire un programme Pascal permettant de simuler le jeu suivant :
Au départ on dispose quelques allumettes sur une table. Chaque joueur peut choisir de prendre 1, 2 ou 3 allumettes en même temps. Le perdant est celui qui retire la dernière allumette.
Exercice « Nombres colombiens »
[modifier | modifier le wikicode](Niveau moyen)
Un nombre colombien, ou auto-nombre, est un entier naturel qui, dans une base donnée, ne peut pas s'écrire sous la forme d'un nombre ajouté à la somme des chiffres de ce nombre. Exemples : 23 n'est pas un nombre colombien, puisqu'il peut être généré par la somme de 16 et de ses chiffres, c’est-à-dire, 23 = 16 + 1 + 6.
20 est un nombre colombien car il n'existe pas une telle somme pour 20.
Écrire un programme Pascal qui permet d’afficher tous les nombres colombiens < 1000.
Exercice « Nombres vampires »
[modifier | modifier le wikicode](Niveau moyen)
Un nombre Vampire est un nombre qui est égal à un produit de ses chiffres.
Exemple : 126 = 21 x 6
Écrire un programme Pascal qui permet de déterminer tous les nombres Vampires de trois chiffres.
Exercice « PGCD et PPCM »
[modifier | modifier le wikicode](Niveau difficile)
Le PGCD de deux nombres entiers M et N supérieurs ou égaux à 2 a pour décomposition en facteurs premiers le produit des facteurs premiers apparaissant à la fois dans la décomposition de M et de N munis du plus petit des exposants trouvés dans la décomposition de M et de N.
Ainsi, si M = 23 x 34 x 52 x 7 et N = 22 x 35 x 73 x 11 alors pgcd (m, n) = 22 x 34 x 7
Le PPCM de deux nombres entiers M et N supérieurs ou égaux à 2 a pour décomposition en facteurs premiers le produit des facteurs premiers apparaissant dans M ou dans N munis du plus grand des exposants trouvés dans la décomposition de M et de N.
Ainsi, si M = 23 x 34 x 52 x 7 et N = 22 x 35 x 73 x 11 alors ppcm (m, n) = 23 x 35 x 52 x 73 x 11
Écrire un programme effectuant les actions suivantes :
- demander deux nombres entiers positifs (a et b) strictement supérieur à 1 à l'utilisateur ;
- déterminer le plus grand des deux nombres (m = max(a,b)) ;
- construire un tableau de tous les nombres premiers inférieurs ou égal à m ;
- construire un tableau de la même taille pour le nombre a donnant l'exposant de chaque nombre premier entrant dans la décomposition en facteurs premiers du nombre a ;
- construire un tableau de la même manière pour le nombre b ;
- créer une fonction prenant en argument le tableau de nombre premiers et un tableau d'exposant et retournant le nombre correspondant ;
- calculer le PPCM par la construction d'un tableau d'exposants en utilisant ceux des nombres a et b, et utiliser la fonction créée précédemment pour afficher le PPCM ;
- calculer le PGCD par la construction d'un tableau d'exposants en utilisant ceux des nombres a et b, et utiliser la fonction créée précédemment pour afficher le PGCD.
Exercice « Nombres riches »
[modifier | modifier le wikicode](Niveau moyen)
Un nombre est riche si un de ses facteurs au moins est répété deux fois ou plus.
Exemples :
4 = 22
90 = 2 x 32 x 5
540 = 22 x 33 x 5
Écrire un programme Pascal qui affiche tous les nombres riches <1000.
Exercice « Expression »
[modifier | modifier le wikicode](Niveau moyen)
Donner une approche directe pour déterminer la formule de l'expression.
Program variables_controle ;
Var i,j,n,expression : integer ;
Begin
Write('Donner n = ') ; readln(n) ;
For i :=1 to n do
For j :=i to n do
Begin
Expression := (* À DÉTERMINER *) ;
Write (‘i=’, i, ’ j=’, j, ’ expression=’, expression) ;
End ;
End.
Exemple d’exécution :
Donner n= 4 i=1 j=1 expression=1 i=1 j=2 expression=2 i=1 j=3 expression=3 i=1 j=4 expression=4 i=2 j=2 expression=5 i=2 j=3 expression=6 i=2 j=4 expression=7 i=3 j=3 expression=8 i=3 j=4 expression=9 i=4 j=4 expression=10
GFDL | Vous avez la permission de copier, distribuer et/ou modifier ce document selon les termes de la licence de documentation libre GNU, version 1.2 ou plus récente publiée par la Free Software Foundation ; sans sections inaltérables, sans texte de première page de couverture et sans texte de dernière page de couverture. |