Université de Lorraine - UHP - ESSTIN                   Département d’informatique                               

2A – Informatique  S4                                              S.D.A.J. / H. Nguyen-Phu  

Cr.: 04/09/2005 (MàN 2A+ & 3A+)                         Dernière MAJ:  2011.01.26  17:16

 

 

 /**********************************************************************/

 /*   C 1.1 : UNE INTRODUCTION AU LANGAGE ALGORITHMIQUE OBJET (L.A.O.) */

 /**********************************************************************/

 

Plan :

 

I - NOTIONS FONDAMENTALES

  Structure générale d’un algorithme

  Les variables et les constantes

  Les types de base

  Les premiers objets informatiques 

      La classe CHAINE

      La classe DATE  

  Les schémas mémoires

  Le type TABLEAU                     

  Exercices d’application

 

II- STRUCTURES DE CONTROLE

  Les instructions conditionnelles

  Les instructions répétitives ou Boucles

  Les boucles emboîtées

  Exercices d’application

 

III- LES FONCTIONS                   

  Les fonctions avec ou sans valeur retournée

  Les paramètres et leur passage

  Les paramètres instance

  Introduction aux fonctions récursives

  Exercices d’application

 

 IV- BIBLIOGRAPHIE

 

 

PARTIE I :  NOTIONS  FONDAMENTALES

 

Ce complément de cours s’articule autour d’un langage algorithmique objet (noté L.A.O.) inspiré du langage Java qui est le langage d’application des trois premières années ESSTIN en informatique. Son avantage est double :

-        il oblige le programmeur à travailler sur papier afin de minimiser la durée de développement et surtout d’éviter des erreurs grossières d’analyse;

-        il facilite aussi le déboguage du logiciel en cours d’implémentation via un langage de programmation comme Java, C#... En effet, il permet de se libérer des contraintes techniques liées à de tels langages.

 

 

1.1 - Structure générale d’un algorithme

 

  Un algorithme est une succession séquentielle d’opérations élémentaires (ou bloc d’instructions) permettant d’obtenir le résultat final désiré pour un problème donné. Ainsi, un algorithme bien conçu, dans des conditions identiques de données d’entrée, doit toujours fournir le même résultat. La structure générale d’un algorithme est donnée par les trois mots clés en majuscule :

 

ALGORITHME  nom_de_l’algorithme   // partie en-tête

DEBUT                             // partie traitement

   un_ou_plusieurs_blocs_d’instructions;

FIN

 

Remarque:  Il est vivement conseillé d’adopter les conventions ci-dessous:

  - les mots-clés du langage algorithmique sont toujours en MAJUSCULES sinon ils doivent être soulignés. Ici, ce sont les délimiteurs DEBUT (ou DEB) et FIN. Les mots-clés ‘DEBUT’ et ‘FIN’ peuvent être remplacés respectivement par ‘{’ et ‘}’.

  - un bloc d’instructions est un tout ou une partie du traitement d’un algorithme entre DEB et FIN ou entre des accolades: accolade ouvrante (‘{‘) et fermante (‘}’).

  - Les commentaires sont des explications textuelles à la suite des deux caractères: //  ou  entre les délimiteurs: 

   /**  

une_ou_plusieurs_lignes_de_commentaires_ici 

        */

 

L’algorithme ci-après illustre l’affichage du message «Salut 2A & 2A+ !» en deux versions:

 

ALGORITHME  bonjour0                      |    ALGORITHME  bonjour1

DEBUT                                     |    DEBUT

   ECRIRE(«Salut »)                       |        ECRIRE(«Salut 2A & 2A+ !\n»)

   ECRIRE(«2A & 2A+ !»)                   |    FIN

   ECRIRE() // pour passer à la ligne.    |    // le caractère ‘\n’ le permet aussi.

FIN                                       |

 

Ainsi, via cet exemple, on constate qu’il y a souvent plusieurs solutions équivalentes possibles pour résoudre un problème donné. La version ‘bonjour0’ va afficher le message en deux lignes  puis descendre à la ligne alors que la seconde version fera la même chose mais en une seule instruction. Il est donc préférable de choisir le plus simple ici (‘bonjour1’).

 

Le mot-clé ECRIRE

-----------------

   Il permet l’affichage, sur le périphérique standard de sortie (i.e. la console de visualisation), des données textuelles (i.e. une ou plusieurs phrases) appelées aussi CONSTANTES de CHAINES de caractères. C’est aussi une procédure d’écriture pour des données numériques (i.e. format à virgule flottante et scientifique …). Elle peut être représentée aussi par le mot-clé ‘AFF’ comme abréviation de «AFFICHER».

 

Le mot-clé LIRE

---------------

   Il permet la saisie de données par l’utilisateur via le périphérique standard d’entrée  (i.e. le clavier). Il est équivalent au mot-clé ‘SAISIR’. Dans la version simplifiée en tant que procédure de lecture, il doit comporter au moins un argument désignant une variable de l’algorithme. Exemple d’emploi:

 

   LIRE(uneVariable)    // <=>  uneVariable  <--  Type.convType(SAISIR_CHAINE()).

 

En Java, le mot-clé LIRE  sera  traduit comme une fonction de lecture d’une chaîne de caractères («SAISIR_CHAINE()»), qui sera convertie au format correspondant (‘Type’) à la variable étudiée nommée ‘uneVariable’ ici. Mais, qu’est-ce que c’est une variable en informatique ?

 

 

1.2 - Les variables et les constantes

 

   Une VARIABLE désigne une place mémoire qui permet de stocker une valeur à manipuler dans l’algorithme. Elle est définie par :

-        un identificateur ou nom unique qui la désigne;

-        un domaine de définition unique appelé aussi TYPE (ou Type);

-        une valeur attribuée au départ et modifiable au cours du déroulement de l’algorithme. Dans le cas contraire, c’est une CONSTANTE et sa valeur doit rester inchangée pendant l’exécution de l’algorithme.

En général, les variables sont  déclarées  juste après la partie en-tête de l’algorithme. La syntaxe de déclaration est :

 

   identificateur : TypeC //<=> Type Courant tel que ENTIER, REEL, BOOL ...

 

Les variables changent de valeur grâce à l’opération d’affectation (‘<-‘).

 

Exemple de l’algorithme simplifié de résolution de l’équation du 1er degré:

 

ALGORITHME   equation1

VARIABLES

   a, b : REEL

DEBUT          

   a <-  2.0      // on affecte  la valeur 2,0  dans la variable ‘a’

   b <- 18.5      // on affecte  la valeur 18,5 dans la variable ‘b’

   ECRIRE(«Solution de  A x + B = 0: », (-b)/a, «\n»)                    

FIN                                     

// L’exécution (i.e. déroulement) de cet algorithme doit fournir l’affichage:

//   Solution de A x + B = 0 : -9,25

 

CONVENTION pour nommer les variables :

-        l’identificateur commence par une minuscule;

-        le nom ne doit pas comporter d’espace;

-        si le nom est constitué de plusieurs mots, éviter les traits d’union en commençant chacun d’eux par une majuscule (Ex. : lePoint, valeurMaximale);

-        il faut bien donner des noms explicites car un algorithme se lit comme un texte normal (proscrire : i1, j2, z3 ...  sauf pour des cas triviaux).

 

Exemple de déclaration d’une constante:

 

ALGORITHME  nom_de_l’algorithme

CONSTANTES

   Pi = 3.14159;  // Pi sera déclarée comme une constante de type REEL

   // Noter l’emploi de ‘=’ au lieu du symbole de l’affectation ‘<-‘

DEBUT

   bloc_instructions

FIN

 

CONVENTION pour nommer les constantes :

-        l’identificateur commence au moins par une majuscule;

-        le nom ne doit pas comporter d’espace;

-        si le nom est constitué de plusieurs mots, éviter les traits d’union en commençant chacun d’eux par une majuscule (Ex. : NombreOr, CtePlank, ORIGINE  etc...);

 

 

1.3-    Les types de base ou types primitifs

 

Les domaines usuels de définition des variables sont appelés aussi des TYPES simples tels que:

-        les entiers ENTIER (noté aussi ENT <=> une partie des entiers relatifs Z),

-        les réels   REEL   ( <=> une partie du corps des réels R),

-        les caractères alpha-numériques  CARACTERE (noté aussi CAR),

-        les booléens BOOL (ou BOOLEEN) qui ne prennent que des valeurs: VRAI ou FAUX appelées aussi constantes logiques prédéfinies.

 

Le tableau ci-dessous montre les seuls opérateurs utilisables pour ces types de base en L.A.O. (ou Langage Algorithmique Objet défini en 2A):

 

Opérateurs                  ENTIER        REEL          CAR          BOOL

 

Moins unaire (-)            Oui           Oui           Non          Non

Plus  unaire (+)            Oui           Oui           Non          Non

Négation logique (NON)      Non           Non           Non          Oui

 

Addition (+)                Oui           Oui           Non          Non

Soustraction (-)            Oui           Oui           Non          Non

Multiplication (*)          Oui           Oui           Non          Non

 

Division entière (DIV)      Oui           Non           Non          Non

Modulo entier    (MOD)      Oui           Non           Non          Non

Division réelle  (/)        Non           Oui           Non          Non

 

Plus grand strictement (>)  Oui           Oui           Oui          Non

Plus petit strictement (<)  Oui           Oui           Oui          Non

Plus grand ou égal     (>=) Oui           Oui           Oui          Non

Plus petit ou égal     (<=) Oui           Oui           Oui          Non

 

Egalité    ( = )            Oui           Oui           Oui          Oui

Différence ( <> )           Oui           Oui           Oui          Oui

 

ET logique (ET)             Non           Non           Non          Oui

OU logique (OU)             Non           Non           Non          Oui

 

Les trois premières opérations sont appelées  « opérateurs unaires » (noté ‘Op1’) alors que le reste du tableau ne concerne que des « opérateurs binaires » (noté ‘Op2’). Ces opérateurs peuvent être définis sous la forme suivante :

 

nomOpérateur :  Domaine1 x ... x Domaine2  -->  DomaineCible

 

Quelques exemples   (A COMPLETER) :

-----------------

NON                : BOOL   -->  BOOL

- (unaire)         : REEL   -->  REEL

...

DIV (entière)      : ENTIER x ENTIER -->  ENTIER  // Ex. : 19 DIV 2 =  9

MOD (reste de DIV) : ENTIER x ENTIER -->  ENTIER  // Ex. : 19 MOD 2 =  1

...

/ (div. réelle)    : REEL   x REEL   -->  REEL    // Ex. : 19./2.   =  9.5

* (mult. réelle)   : REEL   x REEL   -->  REEL    // Ex. : 19.*2.   = 38.0

...

= (Egalité)        : ENTIER x ENTIER -->  BOOL    // «10 = 10»? C’est VRAI.)

>=                 : REEL x REEL     -->  BOOL    // «10. >= 11.»? C’est FAUX.)

<> (Non égal)      : ENTIER x ENTIER -->  BOOL    // «10 <> 10»? C’est FAUX.)

< (Inférieur strict) : CAR  x  CAR   -->  BOOL    // «‘A’ < ‘a’»? C’est VRAI.)

 

Le type ENTIER et le type REEL

------------------------------

Leur emploi est assez intuitif. Dans un premier temps, on ne s’occupera pas des seuils numériques maximaux et minimaux compte tenu de leur représentation sur machine via des registres mémoires à taille limitée en bits.

 

Le type CARACTERE

-----------------

La lettre ‘A’ a la valeur entière égale à 65 (code ASCII en décimal) ou ‘\u0041’ (code UNICODE en hexadécimal) alors que ‘a’ prend la valeur 97 (ou ‘\u0061’). On fera donc attention à la différence entre le caractère ‘5’ et l’entier 5. D’après la table des codes UNICODE (i.e. extension des codes ASCII - cf. menu ‘tpp’ sous Linux), on ne retiendra que les trois propriétés suivantes :

- les codes des caractères majuscules ‘A’, ... ‘Z’ se suivent dans cet ordre;

- les codes des caractères minuscules ‘a’, ... ‘z’ se suivent dans cet ordre;

- de même, les entiers associés aux dix caractères numériques ‘0’ (code 48 ou ‘\u0030’), ..., ‘9’ (code 57 ou ‘\u0039’) se suivent aussi.

 

Ainsi, pour convertir un caractère minuscule en majuscule, on peut lui ajouter la différence qui les sépare:

(CAR) ((ENTIER) ‘b’ + (‘A’ – ‘a’))  vaut ‘B’  <==>  98 + (65 – 97)= 66  = code ASCII de ‘B’

 

De même, pour convertir un caractère majuscule en minuscule, on peut lui ajouter la différence qui les sépare:

(CAR) ((ENTIER) ‘B’ + (‘a’ – ‘A’))  vaut ‘b’   <==> 66 + (97 – 65) = 98  = code ASCII de ‘b’

 

Conversions entre CAR et ENTIER:  Dérouler les algorithmes de conversion ci-dessous via un exemple au choix:

 

ALGORITHME carVersEntier  // car. num.   |  ALGORITHME  entierVersCar

VARIABLES                                |  VARIABLES

   car : CAR;   nb : ENTIER              |     car : CAR;   nb : ENTIER

DEBUT                                    |  DEBUT

   LIRE(car) // parmi ‘0’, ..., ‘9’ ici. |     LIRE(nb) //entre 0 et 127 par ex.

   nb  <-  (ENTIER) car  - (ENTIER) ‘0’  |     car <- (CAR) nb

                          // transtypages explicites ici !

   ECRIRE(nb)                            |     ECRIRE(car)

FIN                                      |  FIN

 

Conversions entre ENTIER et REEL: Dérouler les algorithmes de conversion ci-dessous via un exemple au choix :

 

ALGORITHME reelVersEntier                |  ALGORITHME  entierVersReel

VARIABLES                                |  VARIABLES

   r : REEL;   nb : ENTIER               |     r : REEL;   nb : ENTIER

DEBUT                                    |  DEBUT

   LIRE(r) // Ex. : -3.14159             |     LIRE(nb) // Ex.: -32768,...+32767

   nb  <-  (ENTIER) r                    |     r <- (REEL) nb

   // autre trans-typage explicite       |     //toujours trans-typage explicite

   ECRIRE(nb)                            |     ECRIRE(r)

FIN // Qu’obtient-on avec r = -3.14159 ? |  FIN //Pas de perte d’information ici

 

En effet, certains langages de programmation comme Java pratiquent la conversion implicite entre les types primitifs avec le risque de perte de données (conversion de REEL vers ENTIER par exemple). Il est donc conseillé d’effectuer des transtypages explicites en connaissance de cause.

 

 

Le type logique booléen : BOOL

------------------------------

  La négation logique (NON) retourne toujours l’autre valeur logique par rapport  à celle de l’opérande courante. Les tables de vérité ci-après donnent la réponse «VRAI» ou «FAUX» des opérations logiques fondamentales ‘ET’, ‘OU’:

 

‘ET’     FAUX        VRAI                 ‘OU’     FAUX        VRAI  

FAUX     FAUX        FAUX                 FAUX     FAUX        VRAI

VRAI     FAUX        VRAI                 VRAI     VRAI        VRAI (*)

 

(*) signifie :  «VRAI» OU  «VRAI»  vaut  «VRAI».

 

Exercice 1: Démontrer les règles de MORGAN  via les tables de vérité (A FAIRE):

Si      A, B : BOOL

Alors   NON(A ET B) = NON(A) OU  NON(B);

        NON(A OU B) = NON(A) ET  NON(B);

 

     

Il existe aussi d’autres opérateurs logiques binaires tels que :

 

=>  (Implication) : BOOL x BOOL     -->  BOOL   

<=> (Equivalence) : BOOL x BOOL     -->  BOOL

 

 

Exercice 2: Compléter les tables de vérité ci-dessous (cf. internet):

 

‘=>’     FAUX        VRAI                 ‘<=>’    FAUX        VRAI  

FAUX     VRAI        ...                  FAUX     VRAI        ...

VRAI     ...         VRAI                 VRAI     ...         VRAI

 

Erreur courante (A EVITER) -  Analyser et corriger l’algorithme ci-dessous :

---------------

ALGORITHME  conv_err1

VARIABLES

   c : CAR

DEBUT

   c <-  ‘A’       // OK : c’est correct !

   c <-  18.5      // KO : ‘c’ n’est pas une variable REELLE

   c <-  (5 <= 8)  // KO aussi :  et pourquoi ?

FIN

 

EXPRESSIONS arithmétiques et booléennes

   On pratique, en général, la notation infixée selon la syntaxe :

   Expression  <-  Opérande1  OPERATEUR  Opérande2 

ou

   Expression  <-  Opérande1  OPERATEUR  Opérande2 ... OPERATEUR  OpérandeN

 

Exemples :  x   <-  (-b)/a

            som <-  note1 + note2 + note3

 

On distingue aussi deux autres notations: préfixée (i.e. notation polonaise) et suffixée (ou post-fixée i.e. notation polonaise inverse).

Exemples de notations préfixées:   OU(VRAI, FAUX)

                                   // <=> notation des fonctions habituelles

                                   +(a, b)  // <==>  (a+b) en infixé

Exemples de notations suffixées:   (A, B)OU // <==>  (A OU B) en infixé

                                   (a, b)+  // <==>  (a+b) en infixé etc ...

 

PRIORITES entre les OPERATEURS booléens et arithmétiques

On suivra la convention suivante (priorité croissante dans le sens ascendant):

 

^   - ; + (unaires) ;  NON

|   * ; / ; DIV ; MOD

|   - ; + (binaires)

|   = ; < ; > ; <> ; <= ; >=   // Noter les 6 opérateurs logiques de comparaison (‘Olc’)

|   ET

|   OU

 

Question : Y-t-il une erreur dans les instructions, ci-dessous, extraite d’un algorithme de résolution de l’équation « ax²+ bx+ c= 0 » ?

   x1  <-  (-b –(b²-4.*a*c)^0.5)/2.*a

   x2  <-  (-b +(b²-4.*a*c)^0.5)/2./a

Réponse :

   x1 est incorrect alors que x2 est correct. Pour corriger x1, on peut ajouter des ‘()’ :

   x1  <-  (-b –(b²-4.*a*c)^0.5)/(2.*a)

 

  

Exercice 3: Qu’obtient – on, comme résultats, pour l’algorithme ci-après:

ALGORITHME  bool1

VARIABLES

   b : BOOL

DEBUT

   b <-  (5 <= 8) ;                  ECRIRELN(b)  // <=> ECRIRE(b, «\n»)

   b <-  (VRAI OU VRAI ET FAUX) ;    ECRIRELN(b)

   b <-  (VRAI ET VRAI OU FAUX) ;    ECRIRELN(b)

   b <-  (VRAI OU VRAI) ET FAUX ;    ECRIRELN(b)

   b <-  (VRAI ET FAUX OU FAUX) ;    ECRIRELN(b)

   b <-  (10 >= 10)  OU  (‘f’ > ‘G’) ET (3.14 <> 3.14) ;    ECRIRELN(b)

FIN // On peut modifier la priorité des opérateurs via l’emploi des parenthèses.

 

 

 

1.4 – Les premiers objets informatiques: ‘CHAINE’ et ‘DATE’

 

LA CLASSE  ‘CHAINE’ 

 

   On vient d’étudier les quatre types courants de base à savoir BOOL, CAR, ENTIER et REEL avec la notion de CONSTANTE de CHAINE de caractères lors des affichages via ‘ECRIRE’. Considérons, par exemple, une variable nommée ‘Ch’ associée au type spécial nommé «CHAINE» de caractères qui est muni d’un certain nombre d’opérations élémentaires propres appelées désormais METHODES selon l’interface utilisateur ci-dessous:

 

-------------------------------------------------------------------------------

|  CHAINE    // nom de l’ensemble des objets de type ‘Chaîne de caractères’   |

-------------------------------------------------------------------------------

|+ CHAINE()  // méthode constructive permet de créer une chaîne en mémoire    |

|+ CHAINE(uneCh : CHAINE) //méthode constructive avec un argument de même type|

|+ CHAINE(uneSuiteDeCaractères : TABLEAU de CAR) // autre méthode constructive|

|  ...                                                                        |

|+ versCHAINE(a : TypeC) : CHAINE // convertit un type de base en CHAINE      |

|+ lireCHAINE() : CHAINE //fonction de lecture pour la CHAINE COURANTE        |

|+ modifierIeme(i : ENTIER, c : CAR) // méthode modificatrice pour modifier   |

|                                    // le ième car. de la CHAINE COURANTE    |

|  ...                                                                        |

|+ afficheCHAINE()  // méthode d’écriture de la  CHAINE COURANTE sur l’écran  |

|+ longueurCHAINE() : ENTIER // calcule la longueur de l’instance courante    |

|+ iemeCAR(i : ENTIER) : CAR  // méthode d’extraction du ième caractère  ...  |

|  etc ...                                                                    |

-------------------------------------------------------------------------------

 

Ici CHAINE désigne le nom de l’ensemble des variables du type «Chaînes de caractères». Cet ensemble d’objets est appelé aussi classe. Et la notion de VARIABLE est généralisée maintenant en notion d’instance ou encore objet.

 

La figure ci-dessus montre la présence de trois groupes de méthodes :

1-    Les méthodes constructives (ou constructeurs) permettent l’initialisation éventuelle de l’instance courante. Il y a trois constructeurs dans l’exemple précédent.

2-    Les méthodes modificatrices (ou modifieurs) autorisent le changement du contenu de l’instance courante.

3-    Les méthodes d’accès (ou accesseurs) ne modifient pas le contenu de l’objet courant tout en permettant de décrire certaines de leurs propriétés intrinsèques à savoir le nombre de caractères constitutifs (i.e. sa longueur), la valeur de la ième position (le premier caractère est situé à la position 1).

 

Question:  Dans quel groupe de méthodes (parmi les trois définies précédemment) doit-on spécifier les  nouvelles  méthodes  ci-dessous:

-------------------------------------------------------------------------------

|  CHAINE    // interface utilisateur de la classe CHAINE  (suite)            |

-------------------------------------------------------------------------------

|+ ajoutTete(d : CAR) // ajoute le caractère ‘d’ en tête de la CHAINE courante|

|+ ajoutQueue(f : CAR) // ajoute le caractère ‘f’ à la fin ...                |

|+ concateneCHAINE(chaine2 : CHAINE) // concatène deux chaînes ...            |

|+ extraitSousChaine(deb : ENT, fin : ENT) // entre 2 positions ‘deb’ et ‘fin’|

|  ...                                                                        |

|+ strictInferieur(CHAINE)  // <=>  Olc  ‘<’   des TypeC                      |

|+ strictSuperieur(CHAINE)  // <=>  Olc  ‘>’   des TypeC                      |

|+ inferieurOuEgal(CHAINE)  // <=>  Olc  ‘<=’  des TypeC                      |

|+ superieurOuEgal(CHAINE)  // <=>  Olc  ‘>=’  des TypeC                      |

|+ Egalite(CHAINE)          // <=>  Olc  ‘=’   des TypeC                      |

|+ nonEgal(CHAINE)          // <=>  Olc  ‘<>’  des TypeC                      |

|+ estVide() : BOOL // pour répondre à : «la CHAINE COURANTE est-elle vide ? »|

|  etc ...                                                                    |

-------------------------------------------------------------------------------

 

Réponse (A COMPLETER) :

 

 

Un exemple d’emploi de la classe CHAINE : Compléter les parties manquantes. Que fournit l’algorithme suivant ?

 

ALGORITHME  string1

CONSTANTES

   UnEspace = « » 

// intialisation de la chaîne constante: un blanc <=> « une seule touche sur la barre-espace »

VARIABLES

   nom, prenom, nomComplet : CHAINE 

   // trois déclarations (mais sans réservation d’emplacement mémoire)

DEBUT

   nom <-  RES CHAINE(«Poincaré»)

   // Réserve (RES) des places mémoire pour ‘nom’ qui vaut «  . . .  »

   prenom <- RES CHAINE(«Henri»)

   // RES. des places mémoire pour ‘prenom’ qui vaut «  . . .  »

   nomComplet.concateneCHAINE(prenom)   

   // Ici, l’instance courante est le chaîne ‘nomComplet’

   nomComplet.concateneCHAINE(UnEspace)  // . . .

   nomComplet.concateneCHAINE(nom)       // . . .

   ECRIRE(«Contenu de la chaine courante : »)

   nomComplet.afficheCHAINE()            // . . .

   ECRIRE(«Elle a pour longueur : », nomComplet.longueurCHAINE() )

FIN

 

D’après cet exemple, l’emploi d’une méthode doit toujours indiquer le nom de l’objet courant sur laquelle s’applique l’opération correspondante. La méthode est donc déclenchée par l’instance spécifiée devant le point ‘.’ qui joue le rôle de séparateur. 

 

 

Exercice 4 (A FAIRE): Développer un algorithme permettant de lire une chaîne telle que « Dupond » et de modifier le iemeCar en 6ième position  de cette chaîne pour obtenir « Dupont » en complétant ci-après :

 

ALGORITHME  string2

VARIABLES

   uneChaine : CHAINE 

DEBUT

   ECRIRE(«Algorithme ‘chaine2’ – Taper :  Dupond »)

   uneChaine  <-  uneChaine.lireCHAINE()

   . . .

   . . .

 

FIN

 

 

LA CLASSE ‘DATE’

 

   On considère maintenant la classe DATE comme un ensemble d’objets constitués de triplets de trois entiers pour désigner le jour (jj), le mois (mm) et l’année (aa). Son interface utilisateur, un peu plus détaillée, appelée aussi notation selon la convention UML (« Unified Modeling Language », est décrite par :

 

-------------------------------------------------------------------------------

|  DATE    // nom de la classe ‘DATE’                                         |

-------------------------------------------------------------------------------

|~ annee :  ENTIER  // pour désigner une année selon le calendrier grégorien  |

|~ mois  :  ENTIER  // pour désigner un mois compris entre 01 et 12 au plus   |

|~ jour  :  ENTIER  // pour désigner un jour entre 01 et 31 au plus           |

-------------------------------------------------------------------------------

|+ DATE()  // méthode constructive permet de créer une date  en mémoire       |

|+ DATE(j : ENT, m : ENT, a : ENT) //méthode constructive avec trois arguments|

|+ DATE(uneDate : DATE) //première méthode constructive avec un seul paramètre|

|+ DATE(date: CHAINE) // convertit la chaîne ‘date’ («jj/mm/aaaa») en une DATE|

|  ...                                                                        |

|+ versCHAINE(): CHAINE //convertit la DATE COURANTE en format «jj/mm/aaaa»   |

|+ estEgal(d : DATE) : BOOL // « l’instance courante a la même date que d ? » |

|+ estVide() : BOOL // répond à : « l’instance courante est – elle vide ? »   |

|+ estBissextile() : BOOL // répond à : « ‘annee’ est une année bissextile ? »|

|+ jourSemaine() : CHAINE // retourne un jour de la semaine parmi {Lu,...,Di} |

|+ precede(d : DATE) : BOOL // la DATE COURANTE précède-t-elle ‘d’ ?          |

|+ suit(d : DATE) : BOOL // la DATE COURANTE suit-t-elle ‘d’? Méthode utile ? |

|+ duree(d : DATE) : ENTIER //<=> la durée en jours en la DATE COURANTE et ‘d’|

|+ accesAnnee() :  ENTIER  // pour accéder à la donnée privée ‘annee’         |

|+ accesMois()  :  ENTIER  // pour accéder à la donnée privée ‘mois’          |

|+ accesJour()  :  ENTIER  // pour accéder à la donnée privée ‘jour’          |

|  ...                                                                        |

-------------------------------------------------------------------------------

 

On constate maintenant la présence d’un troisième bloc, appelé bloc des attributs, comportant trois données entières précédées par le symbole ‘~‘ pour indiquer qu’elles sont accessibles dans le paquet (ou dossier) courant. Les méthodes publiques (précédées par ‘+’) sont accessibles de partout; c’est le cas :

-        des méthodes constructives (ou constructeurs) possédant le même nom que la classe,

-        et des fonctions d’accès (les méthodes autres que les constructeurs) ici ?

 

Question : Classer les méthodes publiques ci-dessus parmi trois catégories de méthodes déjà introduites.

Réponse : On ne retrouve, en effet, que deux parmi les trois catégories de méthodes ici : (i) le bloc des constructeurs, (ii) le bloc des méthodes d’accès. Il manque donc le bloc des méthodes modificatrices de chaque champ que le lecteur peut développer à titre d’exercice via un travail personnel de programmation (TPP) ou via un TP-Projet...

 

Notons aussi que le L.A.O. accepte aussi le polymorphisme paramétrique; en effet, le constructeur à un seul paramètre possède ici deux signatures (ou deux versions ou encore deux facettes):

 

 + DATE(DATE)    // premier constructeur avec un paramètre unique du type DATE

 + DATE(CHAINE)  // second constructeur avec un seul paramètre du type CHAINE

 

Un exemple d’emploi de la classe DATE : Compléter les parties manquantes. Que fournit l’algorithme suivant ?

 

ALGORITHME  date1

VARIABLES

   d1, dN : DATE ;  dateNais : CHAINE 

   // déclarations (mais sans réservation d’emplacement mémoire)

DEBUT

   d1 <-  RES DATE(3, 10, 2007)    // A remplacer par la date du jour

   // Réserve (RES) des places mémoire pour ‘d1’ qui vaut «     . . .      »

   ECRIRE(«La date courante », d1.versCHAINE(), « est un : », d1.jourSemaine() )

   dateNais <- RES CHAINE(« ../../.... ») // votre date de naissance par exemple

   dN <-  RES DATE(dateNais)

   ECRIRE(«Votre date de naissance », dN.versCHAINE(), « est un : », dN.jourSemaine() )

   ECRIRE(«Votre année de naissance »,  . . .  , « est une année bissextile ? Rép. = », dN.estBissextile() )

   ECRIRE(«BRAVO ! Jusqu’à aujourd’hui », d1.versCHAINE(), «, vous avez vécu », d1.duree(dN), « jours déjà ! »)

FIN

 

Exemple d’étude de cas (cf. TPP) : DEVELOPPER LES ALGORITHMES DES METHODES MANQUANTES et TRADUIRE toute la classe DATE en Java pour vérification via une classe séparée ‘date_t’.

 

 

1.5 – Les schémas mémoires

 

   Le déroulement d’un algorithme, étape par étape, permet de suivre l’ensemble des variables et des instances ainsi que leurs valeurs respectives; c’est son schéma mémoire. En général, trois parties distinctes sont délimitées pour un schéma mémoire:

·         Le titre de l’algorithme (encadré et centré au début);

·         La partie gauche concerne toutes les variables qui seront représentées soit par une valeur (ou ‘?’ si inconnue) pour des types de base soit par une flèche pour les variables de type objet CHAINE ou DATE, désormais on désigne par références de telles variables de type objet;

·         La partie droite concerne toutes les instances obtenues par la création via l’opérateur ‘RES’ du L.A.O. (autant de cases que d’opérations ‘RES’ pour le moment ...).

 

Exercice 5: Etablir le schéma mémoire pour l’algorithme ‘deuxReferencesPourUneDate’ (A COMPLETER).

 

ALGORITHME  deuxReferencesPourUneDate

VARIABLES

   d1, d2 : DATE

   estBis, apres, idem : BOOL

DEBUT

   d1 <- RES DATE(3, 10,2007)

   d2 <- RES DATE(4, 10,2007)

   idem <- d1.estEgal(d2) 

   estBis <- d1.estBissextile()

   apres <- d1.precede(d2)  // <=>  apres  <-  d2.suit(d1)

   d2 <- d1  // les références d2 et d1 pointent sur l’instance «06/09/2006»

   idem <- d1.estEgal(d2) // c’est une égalité superficielle ici !

   d2.versCHAINE().afficheCHAINE()  // <=> ECRIRE( d2.versCHAINE() )

FIN

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Exercice 6: Etablir le schéma mémoire pour l’algorithme ‘unObjetSansReference’ pour savoir lequel des objets n’est plus pointé par une référence (A COMPLETER).

 

ALGORITHME  unObjetSansReference

VARIABLES

   d1 : DATE

DEBUT

   d1 <- RES DATE(3, 10, 2007)

   d1 <- RES DATE(4, 10, 2007)

   d1.versCHAINE().afficheCHAINE()

FIN

 

 

 

 

 

 

 

 

 

 

 

Exercice 7: Etablir le schéma mémoire pour l’algorithme ‘datesEgales’ (A COMPLETER).

 

ALGORITHME  datesEgales

VARIABLES

   d1, d2 : DATE

DEBUT

   d1 <- RES DATE(3, 10, 2007)

   d2 <- RES DATE(d1)

   ECRIRE(«Les dates », (d1.estEgal(d2) ? « sont » : « ne sont pas »), « égales ! ») 

// Qu’obtient-on ici ? Est-ce une égalité profonde ?

   d1.versCHAINE().afficheCHAINE()  

   ECRIRE( d2.versCHAINE() )

FIN

 

 

 

 

 

 

 

 

Notons l’emploi du premier opérateur ternaire ( ... ? ... : ... ) d’impression de message qui se lit ici simplement: SI  « d1.estEgal(d2) = VRAI »   ALORS  « sont » est affiché;  SINON c’est « ne sont pas » qui sera pris en compte.

Cet opérateur sera détaillé dans la partie suivante sur les instructions conditionnelles.

Par ailleurs, l’emploi du séparateur ‘,’ est assez intuitif ici pour indiquer une concaténation des constantes de chaînes parmi les arguments de ‘ECRIRE’.

 

Exercice 8: Proposer un algorithme permettant de lire une date et d’afficher si elle est bissextile ou non.

Indication:  Faire la rétro-analyse de la  méthode correspondante du fichier ‘DATE.java’.

 

 

1.6 – Le type TABLEAU

 

    Par définition, un tableau est une collection finie de valeurs de type homogène (i.e. même type) et accessibles directement par leur position. Le nombre maximal d’éléments du tableau est sa dimension. Pour accéder à un élément donné, un indice est utilisé pour indiquer son rang pour des tableaux unidimensionnels, appelés aussi vecteurs. Pour des tableaux à deux entrées (appelés aussi matrices si TypeC = BOOL, ENTIER ou REEL), on utilisera deux indices etc ...

 

Exemples de déclaration dans le bloc des VARIABLES:

---------------------------------------------------

  T0 : TABLEAU(1...6) de ENTIER // En Java : int T0[] = new int[6];

 

Ici, dimension = 6 et l’indice part de 1 en L.A.O. mais, en Java, il peut partir de 0. C’est donc une source d’erreur très fréquente lors des implémentations sur machine !

 

  T1 : TABLEAU(0...99) de REEL  

  

Dans cet exemple de tableau unidimensionnel, on déclare et on réserve une dimension maximale de 100 réels et l’indice part de 0 pour faciliter la traduction en Java. On peut séparer aussi la déclaration de la réservation des mémoires :

 

  T1 : TABLEAU[] de REEL     //  float []  T1;

  T1 <- RES  REEL[100]       //  T1 = new float[10];

 

Pour des tableaux à deux dimensions maximales 8 x 8, la déclaration est :

 

  T2 : TABLEAU(0...7)(0...7) de CHAINE

 

pour émuler un échiquier du jeu d’échecs par exemple.

 

 

Utilisation de tableaux

-----------------------  

L’emploi d’une seule variable permet de stocker les six chiffres d’un tirage du Loto ainsi que le chiffre complémentaire dans l’algorithme ci-après.

 

Exercice 9 : Tracer le schéma mémoire du programme suivant:

 

ALGORITHME  loto7

VARIABLES 

    loto : TABLEAU(1...7) de ENTIER

DEBUT

    loto[]  <-  {35, 24, 13, 47, 8, 44,  2} 

    // on peut aussi affecter élément par élément [A EVITER si possible]:

    // loto[1] <- 35; loto[2] <- 24; ... ;  loto[7] <- 2

    Tableaux.TOC(loto, 1, 6) // pour trier par ordre croissant les 6ers chiffres

    //      TOC : Tri par Ordre Croissant de la classe TABLEAU (‘Array’ en anglais)

    ECRIRE(«Tirage trié : », loto[1], loto[2], loto[3], loto[4], loto[5], loto[6])

    ECRIRE(«\nChiffre complémentaire du Loto = », loto[7])

FIN

 

Exercice 10 : Soit la matrice M1 définie par 

        {0  1  1  0}

        {0  0  0  1}

  M1 =  {0  1  1  1}

        {0  0  0  0}

Modifier l’algorithme ‘mat1c’ ci-après pour initialiser le tableau ‘m1’ associé à cette matrice par une affectation « élément par élément ». En général, on évitera de le faire car c’est fastidieux si la dimension du tableau est importante.

 

ALGORITHME   mat1c   // <=> matrice de 1-connexion des 1-graphes  (cf. cours Info.-S3  2nde partie)

VARIABLES 

    m1 : TABLEAU(0...3)(0...3) de ENTIER

DEBUT

       m1 <- {

              {0, 1, 1, 0},

              {0, 0, 0, 1},

              {0, 1, 1, 1},

              {0, 0, 0, 0}

             }

        ...

FIN

 

Exercice 11 : COMMENT ECHANGER DEUX VARIABLES ?

1. Tracer le schéma mémoire de l’algorithme d’échange entre deux variables de type ‘TypeC’.

2. Etendre le programme ‘loto7’ en effectuant un échange entre le 1er et 6è chiffre en accord avec cet algorithme [A TERMINER].

 

ALGORITHME echangeTypeC

VARIABLES

   val1, val2, temp : TypeC  // TypeC = REEL par exemple

DEBUT

   val1 <- 17,5;  val2 <-  11,5

   ECRIRE(“Avant échange : val1 et val2 = ”, val1 , val2, “\n”);

   temp <- val1

   val1 <- val2

   val2 <- temp

   ECRIRE(“Après échange : val1 et val 2 = ”, val1 , val2, “\n”);

FIN

 

Exercice 12 : COMMENT ECHANGER DEUX OBJETS ?

Compléter l’algorithme pour échanger deux objets CHAINE (ou DATE). Et tracer le schéma mémoire correspondant.

 

ALGORITHME echangeObjets

VARIABLES

   val1, val2, temp : CHAINE

DEBUT

   val1 <- RES CHAINE(“Dupond”);     val2 <- RES CHAINE(“Dupont”); 

   . . .

   . . .

   . . .

FIN

 

 

1.7 – Exercices d’application (non corrigés)

 

Exercice 13 : Indiquer les valeurs prises par les constantes et les variables au cours de l’algorithme suivant.

 

ALGORITHME calculFacture

CONSTANTES REELLES

   Tva1 = 1.055;  Tva2 = 1.196

VARIABLES

   valeur, prixHT, prixTTC : REEL

   nombre : ENTIER

DEB

   valeur <-  17.5;  nombre <- 4 

   prixHT <-  valeur * (REEL) nombre

   ECRIRE(« Prix HT = », prixHT, «  € \n»)

   prixTTC <- prixHT * Tva2

   ECRIRE(« Prix TTC= », prixTTC, « € dont le montantTVA= », prixTTC-prixHT  ,«\n»)

FIN

 

Exercice 14 : Ecrire un algorithme qui prend une somme en € et la décompose en billets de 1OO, 50, 20 et 10€.

 

 

 

PARTIE II : STRUCTURES DE CONTROLE

 

 

2.1 – Instructions conditionnelles

 

   Jusqu’à maintenant, l’exécution d’un algorithme est linéaire et se déroule du haut vers le bas et/ou de la gauche vers la droite si les instructions, séparées par le séparateur ‘;’, se situent sur la même ligne. Désormais, on peut ne faire exécuter que certains blocs d’instructions via les instructions conditionnelles.

 

Conditionnelle simple - Syntaxe d’utilisation :

 

   SI  (ConditionRéalisée)  ALORS

        BlocInstructions_1 // exécutée si c’est VRAI

   SINON

        BlocInstructions_2 // exécutée si c’est FAUX

   FSI  //ou FinSI – A ne pas oublier !

 

Notons que la partie ‘SINON’ peut être optionnelle et sera ainsi omise. Si chaque bloc se réduit à une instruction simple, on utilise l’opérateur { ... ? ... : ... } déjà introduit :

 

   { Condition ?  Instruction1  :  Instruction2  }

 

Exemple d’emploi

---------------- 

Soit un extrait de l’algorithme de résolution de « a x²+bx +c = 0 ».

   SI  (a = 0)  ALORS

        TraitementDeEquation1erDegré // Bloc_1 pour résoudre « bx + c = 0 »

   SINON

        TraitementDeEquation2ndDegré // Bloc_2 pour traiter les 3 cas classiques

   FSI 

 

Exercice 15 (A FAIRE) : Ré-écrire l’algorithme ‘valeurAbsolue’ en utilisant l’opérateur ternaire.

ALGORITHME valeurAbsolue

VARIABLES  val, valAbs : REEL

DEBUT

   LIRE(val);  valAbs <- val

   SI  (val < 0)  ALORS

       valAbs <-  -ValAbs 

    // Noter bien le décalage (ou indentation) pour la clarté de l’algo. !

   FSI

   ECRIRELN(«Valeur absolue =  », valAbs)

FIN

 

Question : Peut-on écrire l’algorithme de calcul du minimum (ou maximum) de deux variables de type ‘TypeC’ en employant l’opérateur ternaire ( Condition ? ... : ... ) ? Si oui, expliquer comment via un exemple précis.

Réponse :

ALGORITHME calculMin

VARIABLES  x1, x2, Min : TypeC  // Ex. TypeC = CAR

DEBUT

   LIRE(x1);  LIRE(x2);         // Ex. :  x1 = ‘A’ et  x2 = ’a’

   Min <-- (x1 < x2 ?  x1  :  x2)

   ECRIRE(«Min(x1, x2) =  », Min, «\n»)

FIN

 

ALGORITHME calculMax

VARIABLES  x1, x2, Max : TypeC  // Ex. TypeC = CAR

DEBUT

   LIRE(x1);  LIRE(x2);         // Ex. :  x1 = ‘A’ et  x2 = ’a’

   Max <-- (x1 < x2 ?   ...  :  ...)   // A compléter

   ECRIRELN(«Max(x1, x2) =  », Max)

FIN

 

 

Choix multiples - Syntaxes d’utilisation :

  On utilise de préférence  CAS ...  FCAS  (FinCAS) dans la mesure du possible, sinon  l’emploi des conditionnelles emboîtées est nécessaire. Exemple :

 

 

avec    CAS ...  FINCAS              | avec des conditionnelles emboîtées

                                     |                                  

choix := CAS                         |

      expr1 : Bloc_1; RUPTURE        | SI choix = expr1 ALORS Bloc_1

              // RUPTURE = ‘break;’  |   SINON   // A ne pas oublier !

      expr2 : Bloc_2; RUPTURE        |   SI choix = expr2 ALORS Bloc_2

                                     |     SINON

       ...                           |        ...            

      exprN : Bloc_N; RUPTURE        |       SI Choix= exprN ALORS Bloc_N

                                     |

      AUTRECAS : NOP (ou Bloc_N1)    |          SINON NOP (ou Bloc_N1)

        // NOP : ‘No Operation’      |       FSI

                                     |     FSI

                                     |   FSI

FINCAS  (ou  FCAS)                   | FSI  // Noter les indentations à chaque fois.

 

 

Remarques:

1- Ne pas oublier le mot clé 'AUTRECAS' (appelé aussi ' AUTREMENT' ou 'PAR

   DEFAUT' dans la littérature informatique) pour assurer la complétude.

2- La traduction en Java/C++ de "CAS ... FCAS" n'est pas directe sauf si

   'choix' et 'exprn ' sont des types simples tels que ENTIER ou CAR.

 

Exercice 16 (A FAIRE) :  Développer l’algorithme ‘TraitementDeEquation2ndDegré’ proprement dit ( a <> 0) pour traiter les 3 cas classiques en fonction du discriminant et en minimisant le nombre total d’opérations arithmétiques (cf. complexité temporelle) et aussi en réduisant au minimum l’emploi des variables (cf. complexité spatiale).

 

 

2.2 – Instructions répétitives ou Boucles

 

   On dispose de trois principales boucles itératives, à savoir : "TANT QUE ... FTQ", "POUR ... FPOUR",  "REPETER ... JUSQU'A" ainsi que des schémas algorithmiques équivalents pour des conversions réciproques ci-après. Dans le cas d’une collection d’objets ou de tableaux, on utilisera de préférence la syntaxe suivante:

 

   C :  CollectionOuTABLEAU

   x :  C

   POUR  x  PARMI   C   // « for ... each ... » en anglais

       BlocInstructions

   FPOUR

 

                          BOUCLE ITERATIVE I

 

Boucle "TANT QUE ... FTQ"            | Boucle "POUR   ...     FPOUR"

(Itération conditionnelle)           | (Itération bornée)

                                     |

           i <-- 1                   |

        TANTQUE (i <= n)  FAIRE      | POUR i DE 1 AFAIRE

             -Bloc d'instructions-   |         - Même bloc -

              i <-- i + 1            | FPOUR

        FTQUE    

                    | 

Remarque: La généralisation est possible avec la convention ci-dessous

 

        - Bloc_initialisation -      | POUR    ( - Bloc_initialisation -;

        TANTQUE (NON( Cond_arrêt))   | [TANT QUE] NON(Cond_arrêt) [= VRAI];

          FAIRE  - Trait_progressif- |             - Trait_progressif -

        FTQUE                        |          ) [FPOUR]

Exemple (cf. retro_an.htm):

       x <-- d/2                     | POUR (  x<--d/2;

        TANT QUE(|(x^2-d)/d| > Epsr) |         |(x^2-d)/d| > Epsr;

             FAIRE  x <-- (x+d/x)/2  |         x <-- (x+d/x)/2 ;

        FTQUE                        |       )

 

                          BOUCLE ITERATIVE II

 

Boucle "REPETER ... JUSQU'A"         | Boucle "FAIRE ...    TANTQUE"

(Itération conditionnelle)           | (convention acceptée en TDI)

       REPETER                       |          FAIRE

           - Traitement itératif -   |              - Traitement itératif -

       JUSQU'A ( Cond_arrêt)         |          TANT QUE (NON( Cond_arrêt))

 

Exemple: Lecture de donnée avec filtrage entre deux bornes Binf et Bsup

       REPETER                       |          FAIRE

              ECRIRE('Taper a:')     |                 ECRIRE('Taper a:')

              LIRE(a)                |                 LIRE(a)

       JUSQU'A (a>=Binf ET a<=Bsup)  |          TANTQUE (a<Binf OU a> Bsup)

 

Remarque :  Utiliser le théorème de MORGAN pour passer d'un schéma à l'autre ...

 

                          BOUCLE ITERATIVE III

 

Boucle "REPETER ... JUSQU'A"         | Boucle "Init.; TANTQUE ...   FTQUE"

(Itération conditionnelle)           |           (EMPLOI A EVITER)

                                     |         Cond_arrêt <-- FAUX

       REPETER                       |         TANT QUE (NON(Cond_arrêt ))

           - Traitement itératif -   |              - Traitement itératif -

       JUSQU'A ( Cond_arrêt)         |         FTQUE

 

 

Exercice 17 (A FAIRE) :  Ecrire l’algorithme d’affichage de N premiers entiers naturels impairs en trois versions de boucles itératives : "TANT QUE ... FTQ", "POUR ... FPOUR",  "REPETER ... JUSQU'A". Exemple : Simpair = 1 + 3 + 5 + 7 = 16 (N = 4 ici).

 

Exercice 18 (A FAIRE) :  Ecrire l’algorithme de sommation des N premiers entiers naturels pairs non nuls en trois versions de boucles itératives : "TANT QUE ... FTQ", "POUR   ...     FPOUR",  "FAIRE ... TANTQUE". Exemple : Spair = 2 + 4 + 6 = 12 (N = 3 ici).

 

 

2.3 – Les boucles emboîtées

 

   Si un bloc d’instructions dans une boucle répétitive comporte au moins une boucle interne, on a affaire à des boucles emboîtées à la façon des poupées russes. Par abus de langage, certains ouvrages d’informatique parlent aussi de boucles imbriquées. On les utilise souvent pour manipuler les tableaux à dimension multiple via des boucles « POUR index DE ... A ... FPOUR » emboîtées.

 

Exercice 19 : Etendre l’algorithme ‘mat1c’ pour afficher proprement le contenu du tableau carré ‘m1’ (A TERMINER).

 

 

2.4 – Exercices d’application (non corrigés)

 

Exercice 20 : Développer un algorithme complet de résolution de « a x² + b x + c = 0 » en traitant aussi tous les cas particuliers selon les valeurs réelles des coefficients lus via le clavier et aussi via la ligne de commande.

 

Exercice 21 : Ecrire un algorithme de détermination si un entier lu sur clavier est un nombre parfait (Ex. 6 = 1 + 2 + 3 est un nombre parfait car il est divisible par tous les nombres de la décomposition).

 

Exercice 22 : Ecrire un algorithme qui effectue des conversions réciproques entre Euros et les anciennes monnaies nationales (€ <-> FF, € <-> FB, € <-> DM, ...) de la zone Euro à deux décimales près en arrondissant au plus proche. Les taux de conversions qui sont définis à six chiffres exacts près (Ex : 1 € = 6,55957 FF) peuvent être obtenus via internet ou via les pages économiques du Télétexte de France 2 ...

 

 

 

                PARTIE III :  LES FONCTIONS

 

   Lors de l’étude des premiers objets informatiques, les méthodes qui ont été introduites, sont déjà des fonctions. De même, les opérations fondamentales d’entrée-sortie notées LIRE(...) et ECRIRE(...) sont aussi des fonctions. Cette partie va approfondir leur fonctionnement, leur syntaxe de déclarations des arguments (ou paramètres d’échange) ainsi que leur emploi.

 

3.1 - Les fonctions avec ou sans valeur retournée

 

   Par définition, une FONCTION (ou FCT en abrégé) est un bloc indépendant, ré-utilisable d’instructions. L’appel de la fonction déclenche l’exécution de l’algorithme associé à ce bloc. Une fonction se termine en retournant ou non une valeur unique (i.e. un type précis). La structure générale d’une fonction est :

 

FONCTION  nomFonction( [declarationsEventuellesdArguments] ) : TypeC // En-tête de la fonction

DEBUT

   // Bloc_Instructions comportant au moins une instruction « RETOURNER  uneValeur »

FIN

 

Trois étapes sont nécessaires à l’exécution d’une fonction :

-        Le programme (ou module) appelant, qui peut être une autre fonction, interrompt d’abord son exécution.

-        La fonction appelée exécute son (ses) bloc(s) d’instructions. Dès qu’une instruction « RETOURNER ... » (‘return’ en anglais) est exécutée, la fonction s’arrête.

-        Le module appelant reprend alors son cours normal.

En effet, le contenu de la fonction doit comporter, au moins, une instruction appelée RETOURNER (ou retourne) qui permet l’arrêt de son exécution et le retour au programme appelant sauf pour une procédure dont la définition est donnée par :

 

Une PROCEDURE (ou PROC) est une fonction qui ne retourne aucune valeur (noté  « : Vide » au lieu de « : TypeC »). D’où les deux en-têtes équivalentes d’une procédure;

c’est soit:

   FONCTION  nomProcedure( [declarationsEventuellesdArguments] ) : Vide   // ‘void’ en anglais

soit

   PROCEDURE nomProcedure( [declarationsEventuellesdArguments] )

 

En général, on préfère la seconde notation. Exemple d’une procédure d’affichage de la date du jour:

 

PROCEDURE  attente()  // <=>  FONCTION  attente() : Vide

DEBUT

    today : Date  

    today <-  RES  Date()  // Date  du  jour

    c : CAR

    ECRIRE("\n\n(c)~/2A env. - 'es' utility (current package) - ",  today, "\tSee you !\n")

    ECRIRE(("Press any key to quit ...\n")

    LIRE(c)

FIN

 

Notons aussi la présence des caractères de contrôle ‘\n’ et ‘\t’ qui signifient respectivement « \nouvelle ligne » et « \tabulation (indentation) automatique » comme nouveaux arguments de la méthode ‘ECRIRE()’.

 

Exemple de fonction avec une valeur retournée : resolEquation1(...).

Compléter l’algorithme ci-dessous en fonction des valeurs des arguments venant de l’appelant :

 

FONCTION  resolEquation1(D a : REEL, D b : REEL) : CHAINE  // Soit à résoudre :  a X + b = 0.

                     // ‘D’ : passage de paramètre par valeur peut être omis par défaut.

DEBUT    

    Ch : CHAINE

    SI (a = 0)  ALORS

        SI  ( b = 0 )  ALORS

            Ch <-  « Infinité de solutions (<=> 0/0 ou ‘NaN’ i.e. ‘Not a Number’ en anglais) ! »

        SINON

            SI ( b > 0 ) ALORS

                 Ch <-  « +Infini. »

            SINON

                 Ch <-  « -Infini. »

            FSI

        FSI

    SINON

        Ch <-  «Une solution unique est : »

        Ch <-  Ch.concateneCHAINE( . . . . . . . . . )  // A compléter !

    FSI

    RETOURNER  Ch  // Ici,  ‘uneValeur’ est une CHAINE de caractères.

FIN

 

Exercice 23 (A FAIRE) :

1-    Reprendre la résolution de l’équation du second degré « a2 x² + a1 x + a0 = 0 » (cf. Exercice 20) en utilisant une procédure nommée resolEquation2(D  a2, a1, a0 : REELS) qui fait appel à la fonction resolEquation1 ci-dessus pour traiter le cas particulier où a2 = 0.

2-    Ecrire le programme principal complet de test avec une lecture des données via la saisie par le clavier ou par la ligne de commande en Java.

 

 

3.2 - Les paramètres et leur passage

 

    Un paramètre (ou argument) d’une fonction est aussi une variable locale de cette fonction. Il est affecté, grâce à une copie appropriée dès le début de l’appel, par la valeur passée par le module appelant. On parle alors du mécanisme de passage par valeur (utilisé en Java pour les types primitifs). La variable qui se  situe sur la même place des arguments dans le programme appelant, appelée paramètre effectif, restera inchangé alors que le paramètre formel associé aura une copie de cette valeur et pourra être modifié éventuellement à l’intérieur de la fonction.

 

Exemple de la fonction ‘minimum’ qui retourne le minimum entre deux valeurs.

FONCTION  minimum(D a : Typelt, D b : Typelt) : Typelt  // Typelt est parmi {CAR, ENT, REEL}

DEBUT       // Ici, a et b sont des paramètres formels de la fonction

   SI ( a >= b )  ALORS

      a  <- b

   FSI

   RETOURNER  a   // On peut simplifier par :     RETOURNER  ( (a < b) ? a : b )

FIN

Question : Qu’obtient-on avec l’extrait du module appelant suivant :

   ...

   note1, note2, min : REEL; 

   note1 <- 19,5;  note2 <- 16,5;

   min <- minimum(note1, note2)  // note1 et note2 sont des paramètres effectifs

   ECRIRE(« Valeur minimale entre », note1, « et », note2, « est : », min)

   ...

Réponse (A compléter):

 

D’après l’exemple ci-dessus, la fonction et le module appelant possèdent deux environnements de données totalement distincts même si les paramètres effectifs et formels portent le même type et le même nom. Si on souhaite modifier le contenu du paramètre d’échange au retour, il faut utiliser le mode de passsage par Donnée/Résultat (ou ‘D/R’).

 

Exemple de la procédure ‘maximum’ qui retourne le maximum entre deux valeurs.

PROCEDURE  maximum(D a : Typelt, D b : Typelt, D/R max : Typelt)

DEBUT    

   max <- ( (a < b) ?: a )

FIN

Question : Qu’obtient-on avec l’extrait du module appelant suivant :

   ...

   c1, c2, maxi : CAR; 

   c1  <- ‘b’;  c2 <- ‘B’;   maxi <- ‘ ’

   maximum(c1, c2, maxi)

   ECRIRE(« Valeur maximale entre », c1, « et », c2, « est : », maxi)

   ...

Réponse (A compléter):

 

 

Bien entendu, comme le paramètre ‘D/R’ est unique ici, l’emploi d’une fonction est plus simple:

FONCTION maximum(D a : Typelt, D b : Typelt) : Typelt

DEBUT    

   RETOURNER ( (a < b) ? b : a )

FIN

 

Remarque : On peut imaginer aussi une variante du mode de passage d’argument ‘D/R’, appelée passage en mode Résultat seul (ou ‘R’). Exemple du calcul de moyenne de deux réels via une procédure (Emploi à éviter !) :

PROCEDURE  moyenne(D a : REEL, D b : REEL, R  moy : REEL)

DEBUT    

   moy <- (b + a )/2.

FIN 

// On peut remplacer la procédure ‘moyenne(...)’ par une fonction plus élégante :

FONCTION  moyenne(a, b : REELS) : REEL 

DEBUT     // Ici ‘D’ est omis car, par défaut,  c’est le passage par valeur !

   RETOURNER (b + a)/2.

FIN 

 

En conclusion, la fonction d’entrée « LIRE(uneVariable) » est en effet une procédure avec un passage d’argument en mode ‘D/R’ dont le profil exact est :

   FONCTION LIRE(D/R nomVar: TypeC): Vide  // <=> PROCEDURE  LIRE(D/R  nomVar: TypeC) 

 

De même, l’opération ECRIRE(...) est aussi une procédure avec comme signature :

   PROCEDURE ECRIRE(D CteCHAINE1, D nomVar1, ..., D CteCHAINEs, D NomVars, ...)

 

En effet, la signature d’une fonction permet décrire précisément les éléments suivants:

- le nom de la fonction;

- le type et l’ordre exact de chaque argument;

- le type de la valeur retournée dont ‘Vide’ si c’est une procédure.

 

L’erreur algorithmique, fréquemment  commise, est de permuter par inadvertance cet ordre dans le module appelant: dans le module d’appel de « resolEquation1(a, b) » par exemple, si on permute a et b, un compilateur, aussi sophistiqué soit-il, sera incapable de détecter cette erreur de permutation. On doit donc vérifier, scrupuleusement, la cohérence de l’ordre et du  type des paramètres formels et effectifs de chaque fonction surtout lors de l’implémentation sur machine.

 

Exercice 24 : Compléter l’algorithme de la fonction qui convertit une chaîne de caractères en minuscules et tracer le schéma mémoire correspondant (A TERMINER):

 

FONCTION  majVersMin(D ch : CHAINE) : CHAINE 

VARIABLES

   car : CAR; 

   indice : ENTIER;  

   result : CHAINE

DEBUT    

   result <-  RES  CHAINE(ch);   indice  <-  0

   TANTQUE( indice <  . . . . . . . . . .  ) FAIRE

      car <-  result.iemeCAR(indice)

      SI ((car >= ‘A’) ET (car <= ‘Z’)) ALORS

         car  <-  (CAR) ((ENTIER) car  + (‘a’ – ‘A’))

         // transtypages explicites et implicites ici !

         result.modifierIeme(indice, car)

      FSI

      indice <- indice + 1

   FTQ

   RETOURNER  . . . .

FIN

// Et le module appelant est :

ALGORITHME  v_majVersMin  

VARIABLES

   ch1, ch2 : CHAINE

DEBUT

   ch1 <- RES CHAINE(« MarTin »)

   ch2 <- majVersMin(ch1)

   ch2.afficheCHAINE()

FIN

 

 

3.3 - Les paramètres instance

 

   En Java, le passage par valeur des références d’objet appelés aussi paramètres instance est équivalent en principe au passage en mode ‘D/R’ pour les types primitifs dans d’autres langages comme C++. Etudions une autre version de la méthode de conversion d’une chaîne de caractères en minuscules. On cherche à convertir directement l’instance passée par la valeur de sa référence en paramètre. Le profil de la fonction devient :

 

FCT  majusculeEnMinuscule(D/R ch: CHAINE): Vide //<=> PROC majusculeEnMinuscule(D/R ch : CHAINE)

VARIABLES

   car : CAR; 

   indice : ENTIER;  

DEBUT    

   indice  <-  0

   TANTQUE( indice <  ch.longueurCHAINE() ) FAIRE

      car <-  ch........(indice)  // A COMPLETER

      SI ((car >= ‘A’) ET (car <= ‘Z’)) ALORS

         car  <-  (CAR) ((ENTIER) car  + (‘a’ – ‘A’))

         ch..............(indice, car) // A COMPLETER

      FSI

      indice <- indice + 1

   FTQ

   // « RETOURNER Vide » est optionnel dans une procédure !

FIN

// Et le module appelant est :

ALGORITHME  t_majVersMin  

VARIABLES

   str : CHAINE

DEBUT

   str <- RES CHAINE(« MarTin »)

   majusculeEnMinuscule(str)

   str.afficheCHAINE()

FIN

// Question : Qu’obtient-on comme résultat lors de l’exécution de ce programme en Java ?

//            Et pourquoi ? Comment y remédier ?

// Réponse [A compléter]:

 

 

 

3.4 – Introduction aux fonctions récursives

 

   La séparation et la sauvegarde des environnements de données entre le module appelant et la fonction appelée permettent la conception des fonctions et des modules appelants identiques; autrement dit, une fonction appelée peut être son propre programme appelant. D’où la définition de fonctions récursives:

   Une fonction est dite récursive si elle fait appel à elle-même.

 

La récursion (ou récursivité) est une approche algorithmique puissante et complémentaire de l’itération: elle permet souvent de trouver rapidement des solutions élégantes à des problèmes compliqués (Exemples classiques: Les tours de Hanoï ; les traitements de structures de données auto-référencées comme des listes, des arbres; les algorithmes récursifs de rétro-parcours ...).

 

Pour développer les fonctions récursives, deux conditions sont nécessaires:

-        Connaître la (les) solution(s) triviale(s);

-        Pouvoir décomposer un problème en sous-problèmes de même nature mais de taille plus réduite jusqu’à la condition d’arrêt où le traitement se fait d’une façon immédiate via la solution triviale.

 

La structure algorithmique générale d’une fonction récursive qui retourne une valeur est alors:

 

FONCTION fonctionRecursive(listeArguments): TypeC 

DEBUT    

   SI  ( ConditionArrêt ) ALORS

      RETOURNER  ... // traitement du cas trivial

   SINON

                  // traitement du cas général puis ...

      RETOURNER   ... fonctionRecursive(nouvelleListeArguments)

   FSI     

FIN

 

La structure algorithmique générale d’une procédure récursive est [A compléter]:

PROCEDURE  procedureRecursive(listeArguments)

DEBUT

 

 

 

 

 

 

 

 

FIN

 

Une analogie étroite s’opère, en effet, entre les algorithmes récursifs et les équations obtenues par récurrence telle que: 

-        la somme des n premiers entiers naturels S(n) = n + S(n-1)  avec la cas trivial S(0) = 0.

-        la fonction factorielle  Fact(n) = n!    Fact(n) = n * Fact(n-1) avec Fact(0) = 0 ! = 1.

 

Ainsi il est assez simple de compléter la structure algorithmique générale ci-dessus par les fonctions:

FONCTION sommeNnombres(D  n : ENTIER): ENTIER  // n doit être positif ou nul lors de l’appel !

DEBUT    

   SI  ( n = 0 ) ALORS

      RETOURNER  0  // la solution du cas trivial  S(0) = 0

   SINON

      RETOURNER  n + sommeNnombres(n - 1)

   FSI     

FIN

 

FONCTION Fact(D  n : REEL): REEL  // n doit être positif ou nul lors de l’appel !

DEBUT    

   SI  ( n = 0 ) ALORS

      RETOURNER  ...  // la solution du cas trivial – A COMPLETER !

   SINON

      RETOURNER  n  ...  Fact(n - 1)  // traitement du cas général – A COMPLETER !

   FSI     

FIN

// Et le module appelant contient par exemple :

   ECRIRE(sommeNnombres(3))

   ECRIRE(Fact(3.))

 

Exercice 25 : Tracer les schémas mémoire pour les fonctions récursives ci-dessus (A TERMINER).

 

 

3.5 - Exercices d’application (A FAIRE)

 

Exercice 26 : Développer la fonction  ‘minVersMaj(...)’ qui convertit une chaîne de caractères en majuscules et tracer le schéma mémoire correspondant. Développer aussi un module appelant ‘MAIN’ adéquat.

 

Exercice 27 : Reprendre les exercices de partie II et introduire les fonctions nécessaires.

 

Exercice 28 : Développer le schéma bloc d’une classe unique nommée Entier comportant, entre autre, une méthode permettant l’échange de deux instances de cette classe via le passage par valeur des références d’objets avec clonage.

 

Exercice 29 : Développer le schéma bloc d’une classe unique nommée Point2D (ensemble des points sur le plan ) comportant, entre autre, une méthode permettant l’échange de deux instances de cette classe via le passage par valeur des références d’objets en effectuant des copies profondes (par clonage ou non).

 

Exercice 30 : Traduire en Java tous les algorithmes développés dans cette partie (Exercices 23 à 29).

 

 

 

PARTIE IV :  BIBLIOGRAPHIE

 

LISTE NON EXHAUSTIVE :

======================

BASES MATHEMATIQUES POUR L'INFORMATIQUE

---------------------------------------

VELU J. "Méthodes mathématiques pour l'informatique", 3è édition, DUNOD, 1999;


ALGORITHMIQUE Niv. I

--------------------

AHO A. and ULLMAN J. "Concepts fondamentaux de l'Informatique", Dunod, 1993;

CARDON A. et DABANCOURT C. "Initiation à l'algorithmique objet" (modélisation avec UML et exemples de code en Java et C++), Eyrolles, 2001;

COURTIN J. et KOWARSKI I. "Initiation à l'algorithmique et aux structures de données", vol. 1 & 2, Nouvelle ed., Dunod, 1994, 1995 (cf. CDI de l'ESSTIN: 10 exemplaires);

 

DABANCOURT C. "Apprendre à PROGRAMMER – Algorithmes et conception objet", Eyrolles, 2005;

 

SUPPORT DE COURS

---------------- 

DAVID J.-M. - Cours et TDs « Informatique 1A – S1 » sur ‘arche’, ESSTIN, 2009-2010;

CHEVRIER V.  – Cours et TDs «  Informatique 1A – S2 » sur ‘arche’, ESSTIN, 2009-2010;

NGUYEN-PHU H. - Cours et TDs « Structures de Données & Algorithmique en Java », 2A – S3, ESSTIN, 2009-2010;

GODART  C. – Cours et TDs « Informatique 3A – S5 (UML & SGBD) » sur ‘arche’, ESSTIN, 2009-2010.