Aller au contenu

Programmation Pascal/Version imprimable

Un livre de Wikilivres.

Ceci est la version imprimable de Programmation Pascal.
  • Si vous imprimez cette page, choisissez « Aperçu avant impression » dans votre navigateur, ou cliquez sur le lien Version imprimable dans la boîte à outils, vous verrez cette page sans ce message, ni éléments de navigation sur la gauche ou en haut.
  • Cliquez sur Rafraîchir cette page pour obtenir la dernière version du wikilivre.
  • Pour plus d'informations sur les version imprimables, y compris la manière d'obtenir une version PDF, vous pouvez lire l'article Versions imprimables.


Programmation Pascal

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

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. Une copie de cette licence est incluse dans l'annexe nommée « Licence de documentation libre GNU ».

Introduction

Wikipédia propose un article sur : « Pascal (langage) ».

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 :


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 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 ;
  }

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;
 ...

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.

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;

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;

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).

 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 :

  1. demander deux nombres entiers positifs (a et b) strictement supérieur à 1 à l'utilisateur ;
  2. déterminer le plus grand des deux nombres (m = max(a,b)) ;
  3. construire un tableau de tous les nombres premiers inférieurs ou égal à m ;
  4. 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 ;
  5. construire un tableau de la même manière pour le nombre b ;
  6. créer une fonction prenant en argument le tableau de nombre premiers et un tableau d'exposant et retournant le nombre correspondant ;
  7. 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 ;
  8. 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 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.