4SI
Objectifs
À la fin de ce chapitre, l’élève doit être capable de :
- reconnaître les situations où les structures déjà connues ne suffisent plus ;
- définir un nouveau type groupant des données de natures différentes ;
- déclarer ce type et les objets qui en relèvent, en algorithmique et en Python ;
- accéder à une donnée élémentaire à l’intérieur d’une structure, même emboîtée ;
- construire et parcourir une collection de telles structures ;
- ranger cette collection selon l’une quelconque de ses données.
Plan du chapitre
- Introduction
- L’enregistrement
- L’enregistrement imbriqué
- Le tableau d’enregistrements
- Les enregistrements en Python
- Les erreurs les plus fréquentes
- Retenons
Rappel de 3ème SI. Les parties 1, 2, 3.1, 3.2 et 4 reprennent le cours de 3ème. Nouveau en 4ème : la comparaison de deux dates (paragraphe 3.3) et les questions des applications qui l’utilisent.
1. Introduction
Activité 1
Un professeur veut traiter par programme les informations de sa classe. Pour chaque élève, il doit retenir un numéro, un nom, un groupe, une moyenne, et le fait que l’élève soit interne ou externe.
Avec les seules structures étudiées jusqu’ici, il déclare cinq tableaux :
| Tableau | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
Num | 12 | 7 | 23 | 4 | 18 | 30 |
Nom | Yassine Trabelsi | Nour Bouzid | Mehdi Chaabane | Rania Jendoubi | Skander Ayari | Ines Gharbi |
Groupe | "A" | "B" | "A" | "C" | "B" | "A" |
Moyenne | 13.50 | 16.25 | 9.75 | 12.50 | 17.75 | 11.25 |
Interne | Vrai | Faux | Faux | Vrai | Faux | Vrai |
Questions
- Pourquoi le professeur ne peut-il pas ranger ces cinq informations dans un seul tableau ?
- Le professeur veut classer ses élèves par moyenne croissante. Il trie le tableau
Moyenne. Que devient son programme ? - Que faudrait-il pour que les cinq informations d’un même élève restent liées entre elles ?
Réponse
Un tableau ne contient que des éléments de même type. Or le numéro est un entier, le nom une chaîne de caractères, le groupe un caractère, la moyenne un réel et le statut un booléen : cinq types différents, donc cinq tableaux.
Si le professeur trie le seul tableau Moyenne, les quatre autres ne bougent pas. La moyenne de l’élève placé en première position n’est plus la sienne : elle se retrouve en face du nom d’un autre. Le programme continue de fonctionner et n’affiche aucune erreur, mais toutes les informations sont fausses.
Pour que les cinq informations restent liées, il faudrait qu’elles forment un seul élément, que l’on déplace d’un bloc. C’est ce que permet l’enregistrement.
En détail
Ces cinq tableaux sont dits parallèles : l’élève d’indice i est décrit par Num[i], Nom[i], Groupe[i], Moyenne[i] et Interne[i]. Rien dans le programme ne garantit ce parallélisme ; il repose entièrement sur la discipline du programmeur.
2. L’enregistrement
2.1 Définition
Définition. Un enregistrement est un type de données défini par l’utilisateur, qui groupe un nombre fini de données appelées champs, de types éventuellement différents.
Chaque champ porte un nom et un type. Un champ a exactement les mêmes propriétés qu’une variable du même type : on peut l’affecter, le lire, l’afficher et l’utiliser dans un calcul.
L’enregistrement se distingue du tableau sur deux points : ses éléments peuvent être de types différents, et on les désigne par un nom, non par un indice.
| Tableau | Enregistrement | |
|---|---|---|
| Types des éléments | tous identiques | éventuellement différents |
| Désignation d’un élément | par un indice, T[i] | par un nom, E.Champ |
| Nombre d’éléments | fixé à la déclaration | fixé à la déclaration |
2.2 Déclaration
Le type enregistrement n’existe pas d’avance : c’est l’utilisateur qui le crée. Deux formes de déclaration sont possibles.
Première forme : l’objet seul. Lorsqu’un seul objet est concerné et qu’aucun autre objet ne s’y réfère, l’enregistrement se déclare directement dans le tableau de déclaration des objets.
Syntaxe — tableau de déclaration des objets
| Objet | Type / Nature |
|---|---|
nom_objet | Enregistrement Nom_champ1 : type champ1 Nom_champ2 : type champ2 …Fin |
Seconde forme : un nouveau type. Dès qu’il faut un tableau de tels enregistrements, ou qu’un module en reçoit un en paramètre, le type doit porter un nom : il se déclare alors dans le tableau de déclaration des nouveaux types.
Syntaxe
Tableau de déclaration des nouveaux types
| Nouveau type |
|---|
Nom_type = Enregistrement Nom_champ1 : type champ1 Nom_champ2 : type champ2 … Fin |
Tableau de déclaration des objets
| Objet | Type / Nature |
|---|---|
nom_objet | Nom_type |
Exemple. Le type Eleve décrit un élève de la classe.
TDNT
| Nouveau type |
|---|
Eleve = Enregistrement Num : Entier Nom : Chaîne de caractères Groupe : Caractère Moyenne : Réel Interne : Booléen Fin |
TDO
| Objet | Type / Nature |
|---|---|
E | Eleve |
Remarque importante. Un type se déclare avant d’être utilisé : le tableau de déclaration des nouveaux types précède toujours le tableau de déclaration des objets.
En détail
Le champ Num est un numéro d’ordre dans la classe, compris entre 1 et 35 : c’est un entier, et il le reste. La règle est différente pour un numéro de carte d’identité, qui est un identifiant : on ne fait aucun calcul dessus, et un numéro commençant par zéro perdrait ce premier chiffre s’il était déclaré comme entier. Un tel numéro se déclare en chaîne de caractères.
2.3 Accès à un champ
Un champ se désigne par le nom de l’objet, un point, puis le nom du champ.
Syntaxe algorithmique
nom_objet.nom_champ
Cette écriture désigne une variable ordinaire, du type du champ. Elle s’emploie partout où une variable de ce type est permise.
E.Num ← 12
E.Nom ← "Yassine Trabelsi"
E.Groupe ← "A"
E.Moyenne ← 13.50
E.Interne ← Vrai
Remarque importante. Le point est obligatoire. Écrit seul,
Numdésigne une variable qui n’existe pas ; écrit seul,Edésigne l’enregistrement entier.
2.4 Ce qui se fait champ par champ
Trois opérations ne s’appliquent jamais à un enregistrement entier : la lecture, l’affichage et la comparaison.
Lire et afficher
Chaque champ se lit et s’affiche séparément, comme une variable de son type.
Écrire("Numéro : ")
Lire(E.Num)
Écrire("Nom et prénom : ")
Lire(E.Nom)
Écrire("Moyenne : ")
Lire(E.Moyenne)
Écrire("Élève n° ", E.Num, " : ", E.Nom, ", moyenne ", E.Moyenne)
Lire(E) et Écrire(E) n’ont pas de sens : chaque champ a son type, donc son format de saisie et d’affichage.
Comparer
Deux enregistrements ne se comparent pas : E1 > E2, E1 < E2 et E1 = E2 n’ont pas de sens. Dire qu’un élève est « supérieur » à un autre ne signifie rien tant qu’on n’a pas dit sur quoi porte la comparaison.
On compare donc selon un champ, choisi en fonction de la question posée.
Si E1.Moyenne > E2.Moyenne Alors
Écrire(E1.Nom, " a la meilleure moyenne")
Sinon
Écrire(E2.Nom, " a la meilleure moyenne")
FinSi
Le champ choisi change la réponse. Sur la moyenne, Nour Bouzid, 16.25, l’emporte sur Yassine Trabelsi, 13.50 ; sur le numéro, 12 contre 7, c’est Yassine Trabelsi qui a le plus grand.
Il arrive qu’un seul champ ne suffise pas, parce que la réponse dépend de plusieurs champs à la fois. On les examine alors l’un après l’autre, du plus important au moins important. La comparaison de deux dates, au paragraphe 3.3, en est l’exemple type.
Remarque importante. On ne compare jamais deux enregistrements, on compare leurs champs : un seul lorsqu’il suffit à répondre, plusieurs, pris l’un après l’autre, lorsqu’il ne suffit pas.
2.5 Ce qui se fait d’un bloc : l’affectation
Deux enregistrements de même type s’affectent en une seule instruction. Tous les champs sont recopiés.
Syntaxe algorithmique
E1 ← E2
L’instruction E1 ← E2 équivaut à recopier les champs un à un :
E1.Num ← E2.Num
E1.Nom ← E2.Nom
E1.Groupe ← E2.Groupe
E1.Moyenne ← E2.Moyenne
E1.Interne ← E2.Interne
C’est cette possibilité qui rend l’échange de deux élèves possible dans un tri.
Remarque importante. Les deux enregistrements doivent être exactement du même type. Deux enregistrements qui auraient les mêmes champs sous deux noms de types différents ne s’affectent pas l’un à l’autre.
| Opération sur un enregistrement entier | Permise |
|---|---|
Affecter, E1 ← E2 | oui, entre deux enregistrements de même type |
Lire, Lire(E) | non, champ par champ |
Afficher, Écrire(E) | non, champ par champ |
Comparer, E1 > E2, E1 = E2 | non, selon un champ ou plusieurs champs |
3. L’enregistrement imbriqué
3.1 Principe et déclaration
Un champ peut lui-même être un enregistrement. On parle alors d’enregistrement imbriqué.
Une date de naissance est composée de trois entiers : un jour, un mois et une année. Ces trois entiers n’ont de sens qu’ensemble. Les déclarer comme trois champs séparés de Eleve serait possible, mais on perdrait la possibilité de traiter la date comme un tout.
Remarque importante. Le type du champ se déclare avant le type qui l’utilise. Dans le tableau de déclaration des nouveaux types,
Dateprécède doncEleve.
TDNT
| Nouveau type |
|---|
Date = Enregistrement Jour, Mois, Annee : Entier Fin |
Eleve = Enregistrement Num : Entier Nom : Chaîne de caractères Groupe : Caractère Moyenne : Réel Interne : Booléen Naissance : Date Fin |
C’est la déclaration définitive du chapitre : toutes les parties suivantes l’utilisent.
En détail
La règle « un type se déclare avant d’être utilisé » s’applique donc deux fois : entre le tableau des nouveaux types et celui des objets, et à l’intérieur même du tableau des nouveaux types. Déclarer Eleve avant Date est une erreur sanctionnée.
3.2 Accès en cascade
On descend d’un niveau par point, autant de fois qu’il y a de niveaux.
| Écriture | Désigne | Type obtenu |
|---|---|---|
E.Nom | le nom de l’élève | Chaîne de caractères |
E.Naissance | la date de naissance entière | Date |
E.Naissance.Annee | l’année de naissance | Entier |
E.Naissance.Jour ← 15
E.Naissance.Mois ← 3
E.Naissance.Annee ← 2009
Écrire("Né en ", E.Naissance.Annee)
E.Naissance est un enregistrement de type Date. Les règles du paragraphe 2.4 s’y appliquent : il s’affecte d’un bloc, E1.Naissance ← E2.Naissance, mais il ne se lit, ne s’affiche et ne se compare que champ par champ.
3.3 Comparer deux dates
Pour savoir lequel de deux élèves est le plus âgé, un seul champ de la date ne suffit pas. L’année ne départage pas deux élèves nés la même année ; le mois ne départage pas deux élèves nés le même mois de la même année. C’est le cas annoncé au paragraphe 2.4 : plusieurs champs décident, et on les examine l’un après l’autre, du plus significatif au moins significatif.
Les instructions suivantes rangent dans la variable booléenne avant la réponse à la question « E1 est-il né avant E2 ? ».
Si E1.Naissance.Annee ≠ E2.Naissance.Annee Alors
avant ← E1.Naissance.Annee < E2.Naissance.Annee
Sinon
Si E1.Naissance.Mois ≠ E2.Naissance.Mois Alors
avant ← E1.Naissance.Mois < E2.Naissance.Mois
Sinon
avant ← E1.Naissance.Jour < E2.Naissance.Jour
FinSi
FinSi
Le raisonnement est toujours le même : si le champ le plus significatif diffère, c’est lui qui décide ; sinon, on passe au suivant.
Autre écriture. La même réponse s’obtient en une seule condition, qui énumère les trois façons d’être né avant : une année plus petite, ou la même année et un mois plus petit, ou la même année, le même mois et un jour plus petit.
avant ← (E1.Naissance.Annee < E2.Naissance.Annee)
Ou ((E1.Naissance.Annee = E2.Naissance.Annee)
Et (E1.Naissance.Mois < E2.Naissance.Mois))
Ou ((E1.Naissance.Annee = E2.Naissance.Annee)
Et (E1.Naissance.Mois = E2.Naissance.Mois)
Et (E1.Naissance.Jour < E2.Naissance.Jour))
Les deux écritures donnent toujours le même résultat. Dans la seconde, chaque comparaison est entre parenthèses : Et est prioritaire sur = et sur <.
Exemple.
Naissance de E1 | Naissance de E2 | Champ qui décide | avant |
|---|---|---|---|
| 28/07/2008 | 15/03/2009 | l’année | Vrai |
| 15/03/2009 | 02/11/2009 | le mois | Vrai |
| 20/07/2008 | 28/07/2008 | le jour | Vrai |
| 15/03/2009 | 15/03/2009 | le jour | Faux |
| 02/11/2009 | 15/03/2009 | le mois | Faux |
Remarque importante. Comparer les trois champs séparément est faux. L’expression
(D1.Annee < D2.Annee) Et (D1.Mois < D2.Mois) Et (D1.Jour < D2.Jour)rend Faux pour les dates 28/07/2008 et 15/03/2009, alors que la première est bien antérieure à la seconde.
En détail
La comparaison est stricte : deux dates égales donnent Faux. Deux élèves nés le même jour ne sont nés ni l’un avant l’autre.
La variable avant reçoit à chaque fois le résultat d’une comparaison, jamais Vrai ou Faux écrits en dur.
L’élève le plus âgé est celui dont la date de naissance est la plus ancienne. Les deux superlatifs sont inversés l’un par rapport à l’autre, et c’est une source d’erreur fréquente.
4. Le tableau d’enregistrements
4.1 Principe et déclaration
Un tableau ne contient que des éléments de même type, et le type Eleve en est un : un tableau d’enregistrements est donc un tableau ordinaire, dont chaque case contient un enregistrement entier.
Chaque case contient les six champs d’un élève. Le champ Naissance, lui-même un enregistrement, est encadré à l’intérieur de la case.
Syntaxe — tableau de déclaration des nouveaux types
nom_type_tableau = Tableau de n nom_type_enregistrement
TDNT — à la suite de Date et de Eleve :
| Nouveau type |
|---|
TabEleve = Tableau de 35 Eleve |
TDO
| Objet | Type / Nature |
|---|---|
T | TabEleve |
N | Entier |
La taille déclarée est 35, l’effectif maximal d’une classe. La taille effective N est le nombre d’élèves réellement saisis : 1 ≤ N ≤ 35. Tous les traitements portent sur les indices 0 à N - 1.
4.2 Accès à un champ
On choisit d’abord quel élève, ensuite quelle information. L’indice vient donc avant le point.
| Écriture | Désigne |
|---|---|
T[i] | l’élève de rang i, en entier |
T[i].Moyenne | la moyenne de l’élève de rang i |
T[i].Naissance.Annee | l’année de naissance de l’élève de rang i |
Remarque importante. L’ordre ne s’inverse jamais.
T.Moyenne[i]n’existe pas :Tn’a pas de champMoyenne, ce sont ses éléments qui en ont un.
Exemple de référence. Les six élèves qui suivent servent à vérifier tous les modules de ce chapitre. Ici N vaut 6.
Les valeurs de chaque case suivent l’ordre des champs du TDNT : Num, Nom, Groupe, Moyenne, Interne, puis les trois champs de Naissance.
4.3 Le remplissage
Deux écritures sont possibles, et toutes deux sont justes. La première confie la saisie d’un élève à un module séparé ; la seconde écrit cette saisie directement dans la boucle.
Première écriture : un module de saisie
Un module parcourt le tableau, un autre saisit un élève.
Procédure Remplir (@T : TabEleve, N : Entier)
DEBUT
Pour i de 0 à N - 1 Faire
Saisir_Eleve(T[i])
Fin Pour
FIN
TDOL
| Objet | Type / Nature |
|---|---|
i | Entier |
Saisir_Eleve | Procédure |
Procédure Saisir_Eleve (@E : Eleve)
DEBUT
Répéter
Écrire("Numéro : ")
Lire(E.Num)
Jusqu'à (E.Num ≥ 1) Et (E.Num ≤ 35)
Écrire("Nom et prénom : ")
Lire(E.Nom)
Répéter
Écrire("Groupe (A à D) : ")
Lire(E.Groupe)
Jusqu'à E.Groupe ∈ ["A".."D"]
Répéter
Écrire("Moyenne : ")
Lire(E.Moyenne)
Jusqu'à (E.Moyenne ≥ 0) Et (E.Moyenne ≤ 20)
Écrire("Interne (Vrai / Faux) : ")
Lire(E.Interne)
Écrire("Jour de naissance : ")
Lire(E.Naissance.Jour)
Écrire("Mois de naissance : ")
Lire(E.Naissance.Mois)
Écrire("Année de naissance : ")
Lire(E.Naissance.Annee)
FIN
N.B. On suppose la date de naissance saisie valide.
Remarque importante. Un champ imbriqué se saisit champ terminal par champ terminal, avec son chemin complet :
Lire(E.Naissance.Jour), et nonLire(E.Naissance).
En détail
Les deux procédures sont en passage par référence, @, puisqu’elles modifient ce qu’elles reçoivent. T[i] est passé directement à Saisir_Eleve : c’est un Eleve complet, et c’est bien lui qui est rempli, non une copie.
Les bornes des contrôles de saisie viennent toutes de la situation : 35 élèves au plus dans une classe, quatre groupes de A à D, une moyenne entre 0 et 20.
Seconde écriture : la saisie dans la boucle
La même saisie s’écrit aussi sans module séparé. Le corps de Saisir_Eleve devient le corps de la boucle, et E y est remplacé par T[i].
Procédure Remplir (@T : TabEleve, N : Entier)
DEBUT
Pour i de 0 à N - 1 Faire
Répéter
Écrire("Numéro : ")
Lire(T[i].Num)
Jusqu'à (T[i].Num ≥ 1) Et (T[i].Num ≤ 35)
Écrire("Nom et prénom : ")
Lire(T[i].Nom)
// les autres champs se saisissent de la même façon, sur T[i]
Fin Pour
FIN
TDOL
| Objet | Type / Nature |
|---|---|
i | Entier |
Laquelle choisir
| Situation | Écriture |
|---|---|
| La saisie d’un élève ne sert qu’au remplissage | la seconde, tout dans un module |
| La saisie d’un élève sert ailleurs, par exemple pour ajouter un élève seul | la première, avec un module dédié |
| L’énoncé demande explicitement un module de saisie | la première |
Remarque importante. Un module séparé n’est jamais obligatoire par principe. Il le devient lorsque le même traitement sert à deux endroits, ou lorsque l’énoncé le demande.
Application résolue 1 — La classe
On reprend la classe du paragraphe 4.2 : le tableau T contient les N élèves de la classe, déclarés avec les types Date, Eleve et TabEleve du chapitre.
Travail demandé
- Écrire l’algorithme de la fonction
Nb_Internes(T, N)qui retourne le nombre d’élèves internes. - Écrire l’algorithme de la fonction
Nb_Groupe(T, N, g)qui retourne le nombre d’élèves du groupeg. - Écrire l’algorithme de la fonction
Moyenne_Classe(T, N)qui retourne la moyenne de la classe. - Écrire l’algorithme de la fonction
Position(T, N, x)qui retourne la position de l’élève de numérox, et-1si aucun élève ne porte ce numéro. - Écrire l’algorithme de la fonction
Meilleur(T, N)qui retourne la position de l’élève qui a la meilleure moyenne. - Écrire l’algorithme de la fonction
Plus_Age(T, N)qui retourne la position de l’élève le plus âgé. - Écrire l’algorithme de la procédure
Trier_Moyenne(T, N)qui range les élèves par ordre croissant de leur moyenne. - Un élève propose de ranger la classe du plus âgé au plus jeune en triant sur la seule année de naissance. Écrire l’algorithme de ce tri, puis montrer sur l’exemple qu’il ne répond pas à la question.
- Écrire l’algorithme de la procédure
Trier_Age(T, N)qui range les élèves du plus âgé au plus jeune.
N.B. Le tableau T est supposé déjà rempli. Les numéros, les moyennes et les dates de naissance sont deux à deux distincts.
Voir le corrigé
1. Nb_Internes
On parcourt tout le tableau et l’on compte les élèves qui vérifient la condition.
Fonction Nb_Internes (T : TabEleve, N : Entier) : Entier
DEBUT
nb ← 0
Pour i de 0 à N - 1 Faire
Si T[i].Interne Alors
nb ← nb + 1
FinSi
Fin Pour
Retourner nb
FIN
TDOL
| Objet | Type / Nature |
|---|---|
nb, i | Entier |
Sur l’exemple, cette fonction retourne 3.
En détail
Le champ Interne est déjà un booléen : il est à lui seul la condition du Si. L’écriture Si T[i].Interne = Vrai Alors donne le même résultat, mais compare un booléen à un booléen pour obtenir un booléen.
Le nombre d’élèves à examiner est connu, N : la boucle est un Pour.
2. Nb_Groupe
Seule la condition change par rapport à la question 1. Le groupe cherché est reçu en paramètre.
Fonction Nb_Groupe (T : TabEleve, N : Entier, g : Caractère) : Entier
DEBUT
nb ← 0
Pour i de 0 à N - 1 Faire
Si T[i].Groupe = g Alors
nb ← nb + 1
FinSi
Fin Pour
Retourner nb
FIN
TDOL
| Objet | Type / Nature |
|---|---|
nb, i | Entier |
Sur l’exemple, Nb_Groupe(T, 6, "A") retourne 3.
En détail
Le groupe cherché est un paramètre, non une valeur écrite dans l’algorithme. Une fonction qui ne saurait compter que le groupe "A" devrait être réécrite pour chacun des trois autres.
3. Moyenne_Classe
On cumule les moyennes dans un accumulateur, puis on divise par l’effectif.
Fonction Moyenne_Classe (T : TabEleve, N : Entier) : Réel
DEBUT
s ← 0
Pour i de 0 à N - 1 Faire
s ← s + T[i].Moyenne
Fin Pour
Retourner s / N
FIN
TDOL
| Objet | Type / Nature |
|---|---|
s | Réel |
i | Entier |
Sur l’exemple, la somme des moyennes vaut 81.00 et la fonction retourne 13.5.
4. Position
On avance tant que l’élève n’est pas trouvé et que le tableau n’est pas épuisé.
Fonction Position (T : TabEleve, N, x : Entier) : Entier
DEBUT
i ← 0
Tant que (i < N) Et (T[i].Num ≠ x) Faire
i ← i + 1
Fin Tant que
Si i < N Alors
p ← i
Sinon
p ← -1
FinSi
Retourner p
FIN
TDOL
| Objet | Type / Nature |
|---|---|
i, p | Entier |
Sur l’exemple, Position(T, 6, 23) retourne 2 et Position(T, 6, 99) retourne -1.
En détail
Contrairement aux trois questions précédentes, on ne sait pas d’avance combien d’élèves il faudra examiner : la recherche s’arrête dès que l’élève est trouvé. La boucle est donc un Tant que, et non un Pour.
La valeur -1 est une sentinelle : ce n’est pas un indice possible, elle peut donc porter l’échec de la recherche. Une seule fonction suffit là où il faudrait sinon un booléen pour dire si l’élève existe, et un entier pour dire où il est.
La condition d’arrêt est une conjonction, et l’ordre des deux tests n’est pas libre : i < N doit être écrit en premier, car T[i].Num n’a pas de sens lorsque i vaut N.
5. Meilleur
On garde la position du meilleur élève rencontré, et on la remplace chaque fois qu’on trouve mieux.
Fonction Meilleur (T : TabEleve, N : Entier) : Entier
DEBUT
p ← 0
Pour i de 1 à N - 1 Faire
Si T[i].Moyenne > T[p].Moyenne Alors
p ← i
FinSi
Fin Pour
Retourner p
FIN
TDOL
| Objet | Type / Nature |
|---|---|
p, i | Entier |
Sur l’exemple, cette fonction retourne 4 : Skander Ayari, dont la moyenne est 17.75.
Remarque importante. La fonction retourne une position, non une moyenne. Pour afficher le nom du meilleur élève, on écrit
T[Meilleur(T, N)].Nom. La position donne accès à tous les champs de l’élève ; la valeur seule n’en donnerait aucun.
En détail
La position de référence est initialisée à 0, et la boucle part de 1 : on ne compare jamais le premier élément à lui-même. On n’initialise jamais avec une valeur écrite en dur : sur un champ pouvant être négatif, une initialisation à zéro donnerait un résultat qu’aucun élément ne porte.
6. Plus_Age
La question a la forme de la question 5, mais la comparaison porte sur une date : elle demande les trois champs, examinés l’un après l’autre, comme au paragraphe 3.3.
Cette même comparaison de dates servira de nouveau à la question 9. Écrite deux fois, elle serait deux fois à relire et deux fois à corriger : on la confie donc à une fonction, Avant, que les deux modules appelleront.
Fonction Plus_Age (T : TabEleve, N : Entier) : Entier
DEBUT
p ← 0
Pour i de 1 à N - 1 Faire
Si Avant(T[i].Naissance, T[p].Naissance) Alors
p ← i
FinSi
Fin Pour
Retourner p
FIN
TDOL
| Objet | Type / Nature |
|---|---|
p, i | Entier |
Avant | Fonction |
La fonction Avant reçoit deux dates et rend Vrai si la première est strictement antérieure à la seconde. Son corps reprend les instructions du paragraphe 3.3, écrites sur deux paramètres de type Date.
Fonction Avant (D1, D2 : Date) : Booléen
DEBUT
Si D1.Annee ≠ D2.Annee Alors
test ← D1.Annee < D2.Annee
Sinon
Si D1.Mois ≠ D2.Mois Alors
test ← D1.Mois < D2.Mois
Sinon
test ← D1.Jour < D2.Jour
FinSi
FinSi
Retourner test
FIN
TDOL
| Objet | Type / Nature |
|---|---|
test | Booléen |
Sur l’exemple, Plus_Age retourne 5 : Ines Gharbi, née le 20/07/2008.
En détail
Ines Gharbi et Mehdi Chaabane sont nés le même mois de la même année : c’est le jour qui les départage, 20 contre 28. La troisième branche de Avant est donc bien celle qui décide ici.
On cherche le plus âgé, donc la date la plus ancienne, donc celle pour laquelle Avant rend Vrai. C’est l’inversion signalée au paragraphe 3.3.
La fonction Avant ne comporte qu’un seul Retourner, placé après le dernier FinSi.
7. Trier_Moyenne
On emploie le tri à bulles optimisé. On teste sur le champ Moyenne, et l’on permute les élèves entiers.
Procédure Trier_Moyenne (@T : TabEleve, N : Entier)
DEBUT
NC ← N
Répéter
Permut ← Faux
Pour i de 0 à NC - 2 Faire
Si T[i].Moyenne > T[i + 1].Moyenne Alors
Aux ← T[i]
T[i] ← T[i + 1]
T[i + 1] ← Aux
Permut ← Vrai
FinSi
Fin Pour
NC ← NC - 1
Jusqu'à Non(Permut) Ou (NC = 1)
FIN
TDOL
| Objet | Type / Nature |
|---|---|
NC, i | Entier |
Aux | Eleve |
Permut | Booléen |
Résultat sur l’exemple
Chaque case est restée entière : le numéro, le nom, le groupe et la date ont suivi la moyenne. C’est la réponse au problème posé par l’activité 1.
Remarque importante. On compare un champ,
T[i].Moyenne, mais on échange l’enregistrement entier,T[i]. La variableAuxest donc de typeEleve, et non de typeRéel. DéclaréeRéel, elle ne pourrait recevoirT[i], et échanger les seules moyennes reproduirait exactement la dissociation des tableaux parallèles.
En détail
Le champ de comparaison n’a aucune influence sur la forme de l’algorithme. Trier par nom croissant demanderait T[i].Nom > T[i + 1].Nom, et rien d’autre ne changerait.
Les six moyennes de l’exemple sont deux à deux distinctes : c’est cette unicité qui rend le résultat unique. Sans elle, deux élèves de même moyenne pourraient se retrouver dans un ordre ou dans l’autre selon la méthode de tri employée.
8. Trier sur l’année de naissance
L’année de naissance est un entier : elle se compare directement, avec >, exactement comme la moyenne à la question 7. Seul le chemin d’accès est plus long.
Procédure Trier_Annee (@T : TabEleve, N : Entier)
DEBUT
NC ← N
Répéter
Permut ← Faux
Pour i de 0 à NC - 2 Faire
Si T[i].Naissance.Annee > T[i + 1].Naissance.Annee Alors
Aux ← T[i]
T[i] ← T[i + 1]
T[i + 1] ← Aux
Permut ← Vrai
FinSi
Fin Pour
NC ← NC - 1
Jusqu'à Non(Permut) Ou (NC = 1)
FIN
TDOL
| Objet | Type / Nature |
|---|---|
NC, i | Entier |
Aux | Eleve |
Permut | Booléen |
Résultat sur l’exemple
Les années sont bien rangées. Mais Mehdi Chaabane, né le 28/07/2008, est placé avant Ines Gharbi, née le 20/07/2008, qui est pourtant plus âgée que lui de huit jours. L’année seule ne départage pas deux élèves nés la même année : ils restent dans leur ordre d’origine, qui n’a aucune raison d’être le bon. Ce tri ne répond donc pas à la question.
Remarque importante. Ce n’est pas l’imbrication qui oblige à écrire une fonction de comparaison :
T[i].Naissance.Anneeest un entier, et deux entiers se comparent directement. Ce qui l’oblige, c’est que la réponse dépend de plusieurs champs.
En détail
Le TDOL de Trier_Annee ne contient aucune fonction : la comparaison s’écrit dans la condition du Si. C’est ce qui le distingue de celui de Trier_Age, à la question suivante.
9. Trier_Age
Pour ranger les élèves du plus âgé au plus jeune, il faut comparer les dates entières. La condition du Si devient un appel à la fonction Avant de la question 6.
Procédure Trier_Age (@T : TabEleve, N : Entier)
DEBUT
NC ← N
Répéter
Permut ← Faux
Pour i de 0 à NC - 2 Faire
Si Avant(T[i + 1].Naissance, T[i].Naissance) Alors
Aux ← T[i]
T[i] ← T[i + 1]
T[i + 1] ← Aux
Permut ← Vrai
FinSi
Fin Pour
NC ← NC - 1
Jusqu'à Non(Permut) Ou (NC = 1)
FIN
TDOL
| Objet | Type / Nature |
|---|---|
NC, i | Entier |
Aux | Eleve |
Permut | Booléen |
Avant | Fonction |
Résultat sur l’exemple
Les deux premiers élèves ont changé de place par rapport à la question 8 : Ines Gharbi précède maintenant Mehdi Chaabane.
| Ce que l’on compare | Comment | Exemple |
|---|---|---|
| Un champ de type simple | directement, avec <, > ou = | T[i].Moyenne, T[i].Naissance.Annee |
| Une donnée qui dépend de plusieurs champs | champ par champ, dans une fonction si la comparaison sert plusieurs fois | Avant(T[i + 1].Naissance, T[i].Naissance) |
En détail
Les paramètres de Avant sont donnés dans l’ordre T[i + 1] puis T[i], et non l’inverse : il faut permuter lorsque l’élément suivant est plus ancien que l’élément courant. Écrire Avant(T[i].Naissance, T[i + 1].Naissance) trierait du plus jeune au plus âgé.
Le TDOL de Trier_Age contient Avant, celui de Trier_Annee n’en contient aucune. Un module appelé figure au tableau de déclaration des objets locaux de celui qui l’appelle, et de lui seul.
5. Les enregistrements en Python
5.1 Le dictionnaire
Python ne possède pas de type enregistrement. Trois représentations sont possibles, et une seule convient.
| Représentation | Verdict |
|---|---|
| Le tuple | ses éléments ne peuvent pas être modifiés |
| La classe | hors programme de quatrième année |
| Le dictionnaire | c’est celui que nous retenons |
Un dictionnaire est un ensemble de couples clé : valeur. Les clés sont uniques : elles jouent exactement le rôle des noms de champs.
5.2 Déclaration et accès
Syntaxe Python
nom_enregistrement = dict(champ1 = type1(), champ2 = type2(), …)
E = dict(Num = int(), Nom = str(), Groupe = str(),
Moyenne = float(), Interne = bool())
| Type algorithmique | Python |
|---|---|
| Entier | int() |
| Réel | float() |
| Caractère, Chaîne de caractères | str() |
| Booléen | bool() |
Un champ se désigne par la clé entre crochets, et non par un point.
| Algorithmique | Python |
|---|---|
E.Num ← 12 | E['Num'] = 12 |
Lire(E.Num) | E['Num'] = int(input("Numéro : ")) |
Écrire(E.Num, E.Nom) | print(E['Num'], E['Nom']) |
Remarque importante. Python ne connaît pas le type caractère : un caractère est une chaîne d’un seul symbole. Le champ
Groupese déclare doncstr(), comme le champNom.
Comme en algorithmique, un enregistrement ne s’affiche pas d’un seul coup : on affiche champ par champ, print(E['Nom'], E['Moyenne']).
En détail
Python accepte print(E), qui montre le dictionnaire brut, avec ses accolades et ses clés. Cette écriture n’est pas celle du programme officiel et ne s’écrit pas dans une copie. Elle sert seulement à vérifier son programme sur machine.
5.3 L’imbrication
Un champ peut être un dictionnaire. Deux écritures sont possibles, et donnent le même résultat.
# le type enfant d'abord, comme dans le TDNT
D = dict(Jour = int(), Mois = int(), Annee = int())
E = dict(Num = int(), Nom = str(), Naissance = D)
# ou directement
E = dict(Num = int(), Nom = str(),
Naissance = dict(Jour = int(), Mois = int(), Annee = int()))
L’accès se fait en cascade, un couple de crochets par niveau.
| Algorithmique | Python |
|---|---|
E.Naissance | E['Naissance'] |
E.Naissance.Annee | E['Naissance']['Annee'] |
5.4 Le tableau de dictionnaires
Un tableau d’enregistrements se représente par un tableau numpy dont chaque case reçoit un dictionnaire. Comme en algorithmique, on déclare 35 cases, et l’on n’en remplit que N.
| Algorithmique | Python |
|---|---|
TabEleve = Tableau de 35 Eleve | T = array([dict()] * 35) |
Remplir case par case
On donne à chaque case son propre dictionnaire, puis on remplit ses champs.
from numpy import array
T = array([dict()] * 35)
for i in range(N):
T[i] = dict()
T[i]['Num'] = int(input("Numéro : "))
T[i]['Nom'] = input("Nom et prénom : ")
T[i]['Groupe'] = input("Groupe (A à D) : ")
while not (T[i]['Groupe'] in ["A", "B", "C", "D"]):
T[i]['Groupe'] = input("Groupe (A à D) : ")
Remarque importante. La ligne
T[i] = dict()placée dans la boucle est indispensable. Sans elle, les cases du tableau désignent toutes le même dictionnaire, et le tableau ne contient en réalité qu’un seul élève.
L’instruction array([dict()] * 35) ne crée qu’un seul dictionnaire, et place dans les 35 cases un accès à ce même dictionnaire. Modifier une case modifie donc toutes les autres :
T = array([dict()] * 3)
T[0]['Nom'] = "Yassine"
print(T[1]['Nom'], T[2]['Nom']) # affiche : Yassine Yassine
Le nom n’a été rangé que dans T[0], et pourtant T[1] et T[2] le contiennent aussi. Avec la ligne T[i] = dict() dans la boucle, chaque case a son propre dictionnaire, et T[1] ne contient aucun champ 'Nom'.
Remplir un enregistrement, puis le ranger
On peut aussi remplir d’abord un dictionnaire E, puis le ranger dans la case. C’est la traduction de T[i] ← E.
T = array([dict()] * 35)
for i in range(N):
E = dict(Num = int(), Nom = str())
E['Num'] = int(input("Numéro : "))
E['Nom'] = input("Nom et prénom : ")
T[i] = E
Remarque importante. Cette écriture n’est juste que si
Eest créé dans la boucle, à chaque tour. Créé une seule fois avant la boucle,Eserait le même dictionnaire dans toutes les cases : après la saisie de trois élèves, les trois cases contiendraient le dernier.
En détail
Le phénomène est le même dans les deux cas : en Python, ranger un dictionnaire dans une case, ou l’affecter à une autre variable, ne le recopie pas. Les deux noms désignent le même dictionnaire, et modifier l’un modifie l’autre.
C’est sans conséquence dans une permutation, comme celle d’un tri : Aux = T[i], T[i] = T[i + 1], T[i + 1] = Aux échangent simplement les dictionnaires désignés par les deux cases. C’est une erreur si l’on modifie l’un des deux noms en croyant l’autre intact.
Le même piège existe au niveau imbriqué : si la boucle réutilise un même dictionnaire D comme date de naissance, toutes les dates du tableau n’en font qu’une. Chaque enregistrement imbriqué doit recevoir son propre dict().
5.5 Les modules de l’application 1 en Python
def Moyenne_Classe(T, N):
s = 0
for i in range(N):
s = s + T[i]['Moyenne']
return s / N
def Position(T, N, x):
i = 0
while (i < N) and (T[i]['Num'] != x):
i = i + 1
if i < N:
p = i
else:
p = -1
return p
def Avant(D1, D2):
if D1['Annee'] != D2['Annee']:
test = D1['Annee'] < D2['Annee']
else:
if D1['Mois'] != D2['Mois']:
test = D1['Mois'] < D2['Mois']
else:
test = D1['Jour'] < D2['Jour']
return test
def Trier_Moyenne(T, N):
NC = N
Permut = True
while Permut and (NC > 1):
Permut = False
for i in range(NC - 1):
if T[i]['Moyenne'] > T[i + 1]['Moyenne']:
Aux = T[i]
T[i] = T[i + 1]
T[i + 1] = Aux
Permut = True
NC = NC - 1
En détail
La procédure Trier_Moyenne ne rend rien : un tableau numpy est passé par référence en Python, et les échanges opérés dans la fonction modifient le tableau de l’appelant. C’est le comportement du @ algorithmique.
La boucle Répéter … Jusqu'à n’existe pas en Python. On l’émule par un while dont la variable de contrôle est initialisée à True, ce qui garantit un premier passage. On n’emploie jamais while True suivi d’un break.
Application résolue 2 — Le terrain de foot
Le gardien d’un terrain de foot de quartier note les réservations de la saison 2027. Chaque réservation est décrite par :
Numero: un entier, le numéro de la réservation ;Client: une chaîne de caractères, le nom de la personne ou du club ;Terrain: un caractère,"A"ou"B";Duree: un entier, la durée réservée en minutes ;Payee: un booléen, vrai si la réservation a été réglée ;Date_Res: la date de la réservation, composée d’un jour, d’un mois et d’une année.
Le terrain accepte au plus 300 réservations par saison.
Exemple — cinq réservations :
Travail demandé
- Donner le tableau de déclaration des nouveaux types de cette application.
- Écrire l’algorithme de la fonction
Nb_Impayees(T, N)qui retourne le nombre de réservations non réglées. - Écrire l’algorithme de la fonction
Duree_Terrain(T, N, t)qui retourne la durée totale réservée sur le terraint, en minutes. - Écrire l’algorithme de la fonction
Premiere(T, N)qui retourne la position de la réservation la plus ancienne. - Écrire l’algorithme de la procédure
Trier_Date(T, N)qui range les réservations de la plus ancienne à la plus récente.
N.B. Le tableau T est supposé déjà rempli. Deux réservations n’ont jamais lieu le même jour.
Voir le corrigé
1. Déclarations
| Nouveau type |
|---|
Date = Enregistrement Jour, Mois, Annee : Entier Fin |
Reservation = Enregistrement Numero : Entier Client : Chaîne de caractères Terrain : Caractère Duree : Entier Payee : Booléen Date_Res : Date Fin |
TabReservation = Tableau de 300 Reservation |
Remarque importante. Les trois lignes sont dans l’ordre imposé :
Dateest utilisé parReservation, qui est lui-même utilisé parTabReservation.
En détail
Le champ de la réservation s’appelle Date_Res, et non Date : Date est déjà le nom d’un type. Un champ et un type qui portent le même nom ne se confondent pas pour la machine, mais ils se confondent pour le lecteur. On l’appelle encore moins Jour, puisque Jour est l’un des trois champs qu’il contient : on écrirait alors T[i].Jour.Jour.
2. Nb_Impayees
Fonction Nb_Impayees (T : TabReservation, N : Entier) : Entier
DEBUT
nb ← 0
Pour i de 0 à N - 1 Faire
Si Non(T[i].Payee) Alors
nb ← nb + 1
FinSi
Fin Pour
Retourner nb
FIN
TDOL
| Objet | Type / Nature |
|---|---|
nb, i | Entier |
Sur l’exemple, cette fonction retourne 2 : les réservations 103 et 105.
3. Duree_Terrain
Fonction Duree_Terrain (T : TabReservation, N : Entier, t : Caractère) : Entier
DEBUT
s ← 0
Pour i de 0 à N - 1 Faire
Si T[i].Terrain = t Alors
s ← s + T[i].Duree
FinSi
Fin Pour
Retourner s
FIN
TDOL
| Objet | Type / Nature |
|---|---|
s, i | Entier |
Sur l’exemple, Duree_Terrain(T, 5, "A") retourne 270 minutes et Duree_Terrain(T, 5, "B") retourne 240 minutes.
En détail
Le module cumule et filtre en même temps : un Si à l’intérieur d’une boucle de cumul. L’accumulateur est de type Entier, puisque les durées le sont.
4. Premiere
Le module appelle la fonction Avant. Ce n’est pas une fonction prédéfinie : c’est la fonction écrite à l’application 1, question 6, rappelée ci-dessous.
Fonction Premiere (T : TabReservation, N : Entier) : Entier
DEBUT
p ← 0
Pour i de 1 à N - 1 Faire
Si Avant(T[i].Date_Res, T[p].Date_Res) Alors
p ← i
FinSi
Fin Pour
Retourner p
FIN
TDOL
| Objet | Type / Nature |
|---|---|
p, i | Entier |
Avant | Fonction |
Sur l’exemple, cette fonction retourne 3 : la réservation 104 du Lycée Ibn Sina, le 28/09/2027.
Fonction Avant (D1, D2 : Date) : Booléen
DEBUT
Si D1.Annee ≠ D2.Annee Alors
test ← D1.Annee < D2.Annee
Sinon
Si D1.Mois ≠ D2.Mois Alors
test ← D1.Mois < D2.Mois
Sinon
test ← D1.Jour < D2.Jour
FinSi
FinSi
Retourner test
FIN
TDOL
| Objet | Type / Nature |
|---|---|
test | Booléen |
La fonction est reprise sans modification : elle compare deux dates, quel que soit l’enregistrement qui les contient.
5. Trier_Date
Procédure Trier_Date (@T : TabReservation, N : Entier)
DEBUT
NC ← N
Répéter
Permut ← Faux
Pour i de 0 à NC - 2 Faire
Si Avant(T[i + 1].Date_Res, T[i].Date_Res) Alors
Aux ← T[i]
T[i] ← T[i + 1]
T[i + 1] ← Aux
Permut ← Vrai
FinSi
Fin Pour
NC ← NC - 1
Jusqu'à Non(Permut) Ou (NC = 1)
FIN
TDOL
| Objet | Type / Nature |
|---|---|
NC, i | Entier |
Aux | Reservation |
Permut | Booléen |
Avant | Fonction |
Résultat sur l’exemple
En détail
La variable Aux est de type Reservation, et non de type Date : on compare les dates, mais on échange les réservations entières.
Après le tri, la réservation la plus ancienne occupe la position 0 : sur un tableau trié, la question 4 a une réponse connue d’avance. Ce n’est pas une raison de trier pour la trouver, car le tri coûte beaucoup plus cher qu’un seul parcours.
6. Les erreurs les plus fréquentes
Les erreurs suivantes portent sur les notions du chapitre. Chacune est illustrée par une instruction du cours.
| Instruction erronée | Instruction correcte | Explication |
|---|---|---|
Lire(E) | Lire(E.Num), puis chaque champ | un enregistrement se lit champ par champ |
Écrire(E) | Écrire(E.Num, E.Nom) | un enregistrement s’affiche champ par champ |
Si E1 > E2 Alors | Si E1.Moyenne > E2.Moyenne Alors | on compare selon un champ, jamais deux enregistrements |
Nom ← "Yassine Trabelsi" | E.Nom ← "Yassine Trabelsi" | un champ se désigne par le nom de l’objet, un point et le nom du champ |
T.Moyenne[i] | T[i].Moyenne | l’indice désigne l’élève, le point désigne le champ |
Eleve déclaré avant Date | Date déclaré avant Eleve | un type s’utilise après avoir été déclaré |
Lire(E.Naissance) | Lire(E.Naissance.Jour), puis chaque champ | un champ imbriqué est lui aussi un enregistrement |
E.Nom, en Python | E['Nom'] | en Python, un champ est une clé entre crochets |
oubli de T[i] = dict(), en Python | T[i] = dict() dans la boucle | sans elle, toutes les cases désignent le même dictionnaire |
Remarque importante. Presque toutes ces erreurs ont la même origine : traiter l’enregistrement comme une valeur simple. Un enregistrement se déclare, s’affecte et se range en bloc ; tout le reste se fait champ par champ.
7. Retenons
Enregistrement. Un enregistrement est un type défini par l’utilisateur, qui groupe des champs de types éventuellement différents. Un champ se désigne par le nom de l’objet, un point, puis le nom du champ.
Déclaration. Le type se déclare dans le tableau de déclaration des nouveaux types dès qu’un tableau ou un module s’y réfère. Un type s’écrit toujours avant celui qui l’utilise.
Opérations. Un enregistrement s’affecte d’un bloc, entre deux enregistrements de même type. Il se lit, s’affiche et se compare champ par champ.
Comparaison. On compare selon un champ lorsqu’il suffit à répondre. Lorsque la réponse dépend de plusieurs champs, comme pour une date, on les examine l’un après l’autre, du plus significatif au moins significatif.
Imbrication. Un champ peut être un enregistrement. On y accède en cascade, un point par niveau.
Tableau d’enregistrements. L’indice vient avant le point :
T[i].Champ. La taille déclarée est un maximum ; la taille effectiveNest le nombre d’éléments réellement présents.
Tri. On teste sur un champ, et l’on permute les enregistrements entiers : la variable auxiliaire est du type de l’enregistrement.
Python. L’enregistrement est représenté par un dictionnaire, et le champ par une clé entre crochets. Ranger ou affecter un dictionnaire ne le recopie pas : chaque case d’un tableau doit recevoir son propre dictionnaire.
| Opération | Algorithmique | Python |
|---|---|---|
| Déclarer un type | Eleve = Enregistrement … Fin | E = dict(champ = type(), …) |
| Accéder à un champ | E.Num | E['Num'] |
| Accès en cascade | E.Naissance.Annee | E['Naissance']['Annee'] |
| Affecter un champ | E.Num ← 12 | E['Num'] = 12 |
| Déclarer un tableau | Tableau de 35 Eleve | array([dict()] * 35) |
| Préparer une case | aucune instruction | T[i] = dict() |
| Accéder à un champ d’une case | T[i].Moyenne | T[i]['Moyenne'] |
Teste tes connaissances
Un quiz corrigé question par question, puis des flashcards pour mémoriser les notions du chapitre.

