[[{"title":"Python - Utilisation avancée des tableaux","posi":0},{"text":"
Dans cette séquence nous poursuivons la présentation des tableaux de Python avec des concepts plus avancés.
"},{"text":""}],[{"text":"
Lorsque l’on veut répéter une opération pour chaque élément e d'un tableau t, il est possible d'utiliser l'instruction for directement sur le tableau lui-même, de la manière suivante :
t = [2,5,7,4,8,1]
for e in t:
print(e)
Tester le code ci-dessus.
Dans une telle forme, la boucle énumère les éléments du tableau : elle effectue un tour pour chaque élément de t et l'élément inspecté à un tour donné est associé à la variable e.
Le programme précédent est équivalent à cet autre
programme que nous avons déjà écrit dans lequel la boucle énumère les indices i du tableau t, les éléments correspondants étant récupéré via l'expression t[i].
for i inrange(len(t)):
e = t[i]
print(e)
Tester le code ci-dessus et conclure.
","title":"Itérer sur les éléments d’un tableau"},{"edit":"
Mettre ici les résultats.
"}],[{"text":"
On utilise parfois une boucle for en fournissant directement le tableau des éléments à traiter. Ainsi, le programe
print (\"Directions possibles :\")
for d in [\"Nord\", \"Sud\", \"Est\", \"Ouest\"]:
print(\"x\", d)
Tester le code ci-dessus et conclure.
L'itération directe sur les éléwebts d'un tableau correspond à une vision un peu plus abstraite.
Le tableau y est ramené à une collection d'éléments dans laquelle on à effacé le fait que chaque élément était associé à un certain indice i d'un arrangement linéaire.
En conséquence. cette forme est plus simple à lire comme à écrire. mais n'est applicable que lorsque nous n'avons pas besoin de connaitre l'indice de chaque élément e du tablean t.
","title":"Tableaux vus comme des collections"},{"edit":"
Mettre le résultat ici.
"}],[{"text":"
Supposons que l'on veuille construire un tableau de taille 100 contenant l’entier 3i + 1 dans sa case d'indice i.
Bien sûr, on peut le construire explicitement, avec la définition en extension que nous connaissons déjà
t = [1, 4, 7, 10, ...]
mais c'est extrémement fastidieux.
Une meilleure solution consiste a allouer le tableau dans un premier temps, avec une valeur arbitraire, pour le remplir ensuite avec une boucle.
t = [0] * 100
print(t)
for i inrange(100):
t[i]= 3 * i + 1
print(t)
C'est déjà une bien meilleure solution. Comme c'est là une construction qui revient souvent,
Python propose une syntaxe plus compacte encore pour combiner l'allocation d'un tableau et son remplissage par une boucle.
La voici :
t = [ 3 * 1 + 1 for i inrange(100)]
print(t)
Cette nouvelle construction mélange les crochets, qui explicitent que l'on construit un tableau, et les mots clés de la boucle for de Python, qui explicitent que l'on remplit ce tableau avec une boucle.
On appelle cela la notation par compréhension.
Dans cette construction, le parcours de la variable i n’est pas limité à un intervalle d’entiers construit avec range.
On peut ainsi parcourir un autre tableau déjà construit.
t = [3 * i + 1for i inrange(10)]
print(\"t=\",t)
print( [x * x for x in t] )
","title":" Construire un tableau par compréhension"},{"edit":"
Mettre le résultat ici.
"}],[{"text":"
Enfin, on peut ne conserver que certaines valeurs prises par la variable, en ajoutant une condition booléenne à la compréhension, avec le mot-clé if.
t = [i * i for i inrange(30) if i % 4 == 1]
print(\"t=\",t)
Dans ce cas très particulier où l'on veut construire un tableau contenant les enticrs de i inclus à j exelu, on peut donc écrire
[v for v in range(i, j)]
print([v for v inrange(10, 22,2)])
Il existe cependant une construction plus simple encore, à savoir
list (range(i, j))
print(list (range(10, 22,2)))
La fonction prédéfinie list transforme ici l'ensemble des éléments décrits par range(i, j) en un tableau.
","title":""},{"edit":"
Mettre le résultat ici (code et figure).
"}],[{"text":"
Les tableaux de Pythou peuvent contenir des valeurs arbitraires. En particulier, rien ne nous empêche de construire un tableau dont les éléments sont eux-mêmes des tableaux.
Pour accéder à un entier contenu dans ce tableau de tableaux, on commence par accéder à l'un des trois tableaux, par exemple le troisième avec t[2], puis on accède à l’un de ses éléments, par exemple le deuxième avec [1].
La notation t[2][1] n’a rien de spécifique aux tableaux de tableaux. On le voit bien en isolant t[2] avec des parenthèses
print(\"t[2][1] = \", (t[2])[1])
Nous pouvons le faire aussi en deux étapes :
u = t[2]
print(u[1])
Sur l'exemple ci-dessus, il y a deux dimensions, la première ayant la taille 3 et la seconde la taille 5.
print(len(t),\"x\",len(u))
print(len(t),\"x\",len(t[2]))
Pour un tel tableau, on dira que sa taille est 3 x 5,
On peut se représenter graphiqueruent notre tableau à deux dimensions comme une grille.
0
1
2
3
4
0
1
0
0
0
0
1
1
1
0
0
0
2
1
2
0
0
0
Un tel tableau de tableaux est appelé un tableau à plusieurs dimensions.
Les indices de la première dimension sont indiqués verticalement, à gauche, et les indices de la seconde dimension sont indiqués horizontalement, en haut.
Pour modifier un élément, on utilise une affectation, exactement comme on le ferait avec un tableau à une dimension.
print(\"t = \", t)
t[1][3] = 7
print(\"t = \", t)
Là encore, cette affectation peut être décomposée, mentalement ou explicitement, comme un accès au tableau t[1] puis à l'affectation de son quatrième
élément.
Un tel tableau à deux dimensions, dont toutes les lignes ont la même taille, est parfois appelé une matrice (notamment dans un contexte mathématique).
Le concept de tableau à plusieurs dimensions est cependant plus général, car il peut y avoir plus que deux dimensions (un tableau de tableaux de tableaux, par exemple).
Par ailleurs. rien n'empêche les différents sous-tableaux d'avoir des tailles différentes, comme dans l'exemple suivant.
t1 = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
print(t1)
On s'éloigne alors du concept de matrice. On peut également rapprocher un tableau à deux dimensions de la notion familière de tableur (Excel, Google sheet).
","title":"Tableaux à plusieurs dimensions"},{"edit":"
Mettre le résultat ici (code et figure).
"}],[{"text":"
Pour construire un tableau de tableaux, on peut avantageusement se servir de la construction par compréhension introduite précédemment.
t = [[0] * 5for i inrange(3)]
print(t)
Il est important de bien comprendre qu'on a ici évalué trois fois l'expression [0] * 5, pour chaque valeur de i, en obtenant à chaque fois un nouveau tableau.
Le tableau t est un tableau de taille 3, donc chaque valeur est un tableau (différent) de taille 5.
","title":"Construction de tableaux à plusieurs dimensions"},{"edit":"
Mettre le résultat ici (code et figure).
"}],[{"text":"
Erreurs. Il pourrait être tentant de construire le tableau t en écrivant simplement :
t = [[0] * 5] * 3
print(t)
On à même l'impression que cela donne le bon résultat si on observe la valeur de t affichée par Python :
c'est-à-dire que l'on à modifié simultanément le deuxième élément des trois lignes.
Ce n'est probablement pas ce que l’on voulait faire.
","title":""},{"edit":"
Mettre le résultat ici (code et figure).
"}],[{"text":"
Pour parcourir tous les éléments d’un tableau à plusieurs dimensions, on va naturellement utiliser des boucles imbriquées, avec chaque boucle parcourant une dimension.
Si on reprend notre tableau t de taille 3x5 et qu'on souhaite par exemple faire la somme de tous ses éléments.
D'une façon équivalente, on peut utiliser également le parcours d'un tableau avec la méthode suivante :
t1 = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
print(t1)
s = 0
for l in t1:
for e in l:
s += e
print(s)
Ici, la variable l parcourt les lignes du tableau t à deux dimensions ct la variable e parcourt les éléments de chaque ligne. En particulier, les indices i
et j ont disparu (ils sont cachés derrière la construction for).
Une autre différence, plus significative, est que nous n'avons plus besoin de connaître les différentes dimensions (3 et 5 dans cet exemple), même si on aurait pu remplacer 3 ot 5 par len(t) et len(t[i]), respectivement, dans le premier programme.
","title":"Parcours d’un tableau à plusieurs dimensions"},{"edit":"
Mettre le résultat ici (code et figure).
"}],[{"text":"
Que construit l’expression [i % 3 for i in range(100)]?
(On rappelle que i % 3 est le reste de la division euclidienne de i par 3.)
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
Un tableau de taille 100 répétant la sé-
quence 0, 1, 2:
[0, 1, 2, 0, 1, 2, 0, 1, 2, ...]
"}],[{"text":"
Écrire un programme qui construit un tableau t de taille 11 x 11, tel que t[i][j] contient i x j.
","title":"Exercice : Tables de multiplication"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
Il faut utiliser une double compréhension :
t = [[i * j for j inrange(11)] for i inrange(11)]
print(t)
"}],[{"text":"
Écrire un programme qui transforme un tableau t de 64 cases en un tableau m à deux dimensions, de taille 8 x 8, tel que :
m[i][j] = t[i x 8 + j] pour tout 0 < i,j <8.
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
On peut le faire avec une double compréhension :
t = [i for i inrange(64)]
print(t)
m = [[t[8 * i + j] for j inrange(8)] for i inrange(8)]
print(m)
"}],[{"text":"
Écrire un progrannne qui transforme un tableau m à deux dimensions, de taille 8 x 8, en un tableau t à une dimensions, de taille 64. tel que t[8 * i + j] = m[i][j] pour tout 0 < i,j < 8 .
Indication :
Commencer par créer le tableau t puis le remplir avec une double boucle.
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
m = [[j + 8*i for j inrange(8)] for i inrange(8)]
print(m)
t = [0] * 64
for i inrange(8):
for j inrange(8):
t[8 * i + j] = m[i][j]
print(t)
"}],[{"text":"
Écrire un programme qui crée un tableau à deux dimensions de taille 30 x 30 contenant des entiers tirés au hasard entre 1 et 9999, puis l'affiche.
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
On peut le faire avec une double compréhension.
from random import randint
tab = [[randint(1, 9999) for _ inrange(30)] \\
for _ inrange(30)]
print(tab)
"}],[{"text":"
Compléter le programme précédent pour calculer et afficher l'élément maximum de ce tableau.
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
On initialise une variable maximum à tab[0][0]. Ensuite on parcourt chaque ligne et chaque élément de chaque ligne pour le comparer avec le maximum.
from random import randint
tab = [[randint(1, 9999) for _ inrange(30)] \\
print(tab)
#cherche le maximum de tab
maximum = tab[0][0]
for i inrange(30):
for j inrange(30):
if tab[i][j] > maximum:
maximum = tab[i][j]
print (\"le maximum est\", maximum)
"}],[{"text":"
Écrire une fonction qui prend cu paramètre un tableau à deux dimensions de hauteur et de largeur non nulles et qui renvoie le maximum parmi les minima de chaque ligne.
Indication : on pourra utiliser une fonction auxiliaire pour calculer le mininnun d'une ligne.
Tester avec le tableau suivant :
from random import randint
tab = [[randint(1, 9999) for _ inrange(30)] \\
print(tab)
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
Comme chaque ligne contient au moins un élément,
on peut prendre le premier élément pour initialiser la variable minimum dans
la fonction auxiliaire de calcul du minimum.
defminimum(ligne) :
minimum = ligne[0]
for e in ligne:
if e < minimum:
minimum = e
return minimum
De même, comme le tableau contient au moins une ligne, on peut prendre le minimum de la première ligne pour initialiser la variable maximum dans le calcul du maximum.
defmaximin(tab):
maximum = minimum(tab[0])
for i inrange(1, len(tab)):
min_i = minimum(tab[i])
if maximum < min_i:
maximum = min_i
return maximum
print(maximin(tab))
"}],[{"text":"
Le Puissance 4 est un jeu bien connu à deux joueurs qui se joue sur une grille verticale de six lignes et sept colonnes.
À tour de rôle, chaque joueur fait tomber un pion de sa couleur dans une colonne de son choix non encore pleine.
Le premier joueur qui aligne quatre pions de sa couleur, horizontalement, verticalement où en diagonal, gagne la partie.
La partie est nulle si la grille ost totalement remplie sans qu'aucun joueur ne gagne.
Pour représenter une grille de ce jeu, on utilise un tableau à deux dimensions de taille 6 x 7, la première dimension représentant les lignes et la seconde les colonnes.
Une ligne est notée l et prend une valeur entre 0 et 5, la ligne 0 étant situéc en bas: une colonne est notée c et prend une valeur entre 0 et 6, la colonne 0 étant située à gauche.
Un joueur est noté j et prend la valeur 1 ou 2.
Dans une grille, notée g, la valeur 0 représente une
case vide et la valeur 1 ou 2 représente un pion du joueur correspondant,
1. Écrire une fonction grille_vide() qui renvoie une grille vide.
2. Ecrire une fonction affiche(g) qui affiche une grille, avec le caractère . pour une case vide, le caractère X pour le joueur 1 et le caractère O pour le joueur 2. On prendra soin de bien afficher les lignes de haut en bas
Tester avec :
affiche(gr)
Résultat :
.......
.......
.......
.......
.......
3. Ecrire une fonction coup_possible(g, c) qui renvoie un booléen indiquant s'il est possible de jouer dans la colonne c.
4. Écrire une fonction jouer(g, j, c) qui joue un coup du joueur j dans la colonne c, en supposant que la colonne c n'est pas pleine.
5. Écrire trois fonctions
- horiz(g, j, l, c)
- vert(g, j, l, c)
- et diag(g, j, l, c)
qui déterminent respectivement s'il y à un alignement de quatre pions du joueur j à partir de la case (l, c).
6. Ecrire une fonction victoire(g, j) qui renvoie un booléen indiquant si le joueur j à gagné.
7. Ecrire une fonction match_nul(g) qui renvoie un booléen indiquant s'il y a match nul, c'est-à-dire si la grille est totalement remplie.
Indication : on peut se contenter d'examiner la ligne du haut.
8. Ecrire une fonction coup_aleatoire(g, j) qui joue un coup aléatoire légal pour le joueur j, en supposant que la grille n'est pas pleine. On prendra soin de ne pas favoriser de colonne particulière.
9. Écrire une fonction jouer_auto(g) qui fait jouer deux adversaires aléatoirement à tour de rôle, en affichant la grille après chaque coup, et qui s'arrête dès qu'un joueur gagne ou que la partie est nulle.
10. Écrire enfin une fonction jouer_fonction(g) qui permet à l'utilisateur de jouer contre un adversaire qui joue aléatoirement, en affichant la grille après chaque coup et le résultat en fin de partie.
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
defgrille_vide():
return [[0] * 7for _ inrange(6)]
gr = grille_vide()
print(gr)
"},{"solution":"
defaffiche(g):
for l inrange(0, 6):
# on affiche de haut en bas
for c inrange(0, 7):
if g[l][c] == 0:
print(\".\", end=\"\")
elif g[l][c] == 1:
print (\"X\", end=\"\")
else :
print(\"O\", end=\"\")
print()
print()
affiche(gr)
"},{"solution":"
bas = 0
haut = 5
defcoup_possible(g, c):
return g[haut][c] == 0
"},{"solution":"
defjouer(g, j, c):
\"\"\"le joueur j joue dans La colonne c\"\"\"
l = haut
while l > bas and g[l - 1][c] == 0:
l=l-1
g[l][c] = j
"},{"solution":"5.
defhoriz(g, j, l , c):
for i inrange(4):
if g[l][c + i] != j: returnFalse
returnTrue
defvert(g, j, l, c):
for i inrange(4):
if g[l + i][c] != j: returnFalse
returnTrue
defdiag(g, j, l, c):
for i inrange(4):
if g[l + i][c+i] != j: returnFalse
returnTrue
"},{"solution":"6.
defvictoire(g, j):
for l inrange(6):
for c inrange(7):
if c < 4and horiz(g, j, l, c) \\
or1 < 3and vert(g, j, l, c) \\
or c < 4and1 < 3and diag(g, j, l, c):
returnTrue
returnFalse
"},{"solution":"7.
defmatch_nul(g):
\"\"\"détermine si la grille est pleine\"\"\"
for c inrange(6):
if g[haut][c] == 0:
returnFalse
returnTrue
"},{"solution":"8.
defcoup_aleatoire(g, j):
\"\"\"le joueur j joue un coup légal aléatoire\"\"\"
whileTrue:
c = randint(0, 6)
if coup_possible(g, c):
jouer(g, j, c)
return
Dans cette dernière fonction, le return permet de terminer la fonction dès qu’un coup possible à été trouvé et joué.
"},{"solution":"9.
defjouer_auto(g) :
joueur = 1
whilenot(match_nul(g)) :
coup_aleatoire(g, joueur)
affiche(g)
if victoire(g,joueur) :
print(joueur,\"a gagné!\")
#pour sortir de la boucle while
return
#change de joueur 1 coup sur 2
if joueur == 1 :
joueur = 2
else :
joueur = 1
sleep(0.1)
jouer_auto(gr)
"},{"solution":"
defcoup_manu(g, j):
\"\"\"le joueur j joue un coup légal\"\"\"
whileTrue:
c = int(input(\"colonne entre 0 et 5 :\"))
if coup_possible(g, c):
jouer(g, j, c)
return
defjouer_manu(g) :
joueur = 1
whilenot(match_nul(g)) :
if joueur == 1 :
coup_aleatoire(g, joueur)
else :
coup_manu(g, joueur)
affiche(g)
if victoire(g,joueur) :
print(joueur,\"a gagné!\")
#pour sortir de la boucle while
return
#change de joueur 1 coup sur 2
if joueur == 1 :
joueur = 2
else :
joueur = 1
sleep(0.1)
jouer_manu(gr)
"}],[{"text":"
Exercice 129 Écrire une fonction dessine qui prend en paramètre un tableau de 2 dimension contenant des booléens, et qui le dessine à l’aide de Turtle en représentant chaque occurrence dé True par un carré noir. Ainsi, le tableau t1 donnera le dessin ci-dessous.
2.1. Ecrire cette fonction en utilisant une fonction itérative (boucle for).
2.2. Ecrire cette fonction en utilisant une fonction récursive.
Rappels; si s = "MDCLXVI"
print(s[0]) -> "M"
print(s[1:]) -> "DCLXVI"
print(dic[s[0]]) -> 1000
Exemples :
print(rom_to_dec(""))
print(rom_to_dec("I"))
print(rom_to_dec("V"))
print(rom_to_dec("X"))
print(rom_to_dec("L"))
print(rom_to_dec("C"))
print(rom_to_dec("D"))
print(rom_to_dec("M"))
print(rom_to_dec("MDCLXVI"))
résultats :
0 1 5 10 50 100 500 1000 1666
Exercice 3 : Modularité
Nous avons le programme suivant qui calcule les solutions d'un polynôme du second degré (a.x2+b.x+c) :
from math import *
def solutpoly2(a,b,c) :
d = b**2 - 4 * a * c
if d < 0 :
return None
elif d == 0 :
return - b / (2*a)
else :
x1 = (- b + sqrt(d) ) / (2*a)
x2 = (- b - sqrt(d) ) / (2*a)
return (x1,x2)
#Test de la fonction
print(solutpoly2(1,2,3))
print(solutpoly2(1,4,4))
print(solutpoly2(1,0,-1))
Nous souhaitons décomposer ce programme en 4 fonctions (méthodes) pour qu'il soit plus facile à débuger.
Nous utiliserons comme fonction principale la fonction suivante :
def solutpoly2(a,b,c) :
d = delta(a,b,c)
if d < 0 :
return None
elif d == 0 :
return sol1(a,b)
else :
return sol2(a,b)
3.1. Ecrire la fonction delta.
3.2. Ecrire la fonction sol1
3.3.Ecrire la fonction sol2.
3.4. Tester votre code avec :
print(solutpoly2(1,2,3))
print(solutpoly2(1,4,4))
print(solutpoly2(1,0,-1))
et vérifier que vous obtenez les mêmes résultats que la première fonction solupoly2.
Exercice 4 : Modularité
Ecrire un module SurfaceAire.py qui est composé de 4 fonctions avec le cahier des charges suivant :
4.1. La fonction sdisque qui a comme paramètre le diamètre du disque et qui retourne sa surface.
4.2. La fonction vcylindre qui a comme paramètre le diamètre et la hauteur d'un cylindre et qui retourne son volume. Attention, cette fonction doit utiliser la fonction sdisque.
4.3. La fonction scarre qui a comme paramètre un coté du carré et qui retourne sa surface.
4.4. La fonction vpyramide qui a comme paramètre le coté et la hauteur de la pyramide et qui retourne son volume. Attention, cette fonction doit utiliser la fonction scarre.
Si vous tester avec les instructions suivantes :
print(sdisque(2))
print(vcylindre(2,4))
print(scarre(3))
print(vpyramide(3,4))
vous obtenez :
3.141592653589793 12.566370614359172 9 12.0
Ecercice 5 : POO
5.1. Ecrire une classe de base nommée Vehicule.
Un Vehicule possède trois attributs :
annee_achat de type int ;
prix_achat de type float;
prix_courant de type float:
Et les méthodes :
__affiche__() permet d’afficher les trois attributs : Annee_achat, prix_achat et prix_courant ;
__calculePrix__() permet de calculer le membre prix_courant d’un Vehicule à l’aide de la formule suivante : prix_courant = (1 - ( (2020 - annee_achat) * 0.01 )) * prix_achat
5.2. Tester la classe avec les instructions suivantes :
mavoiture = Vehicule(2010,20000,12000)
print(mavoiture.__affiche__())
print(mavoiture.__calculePrix__())
Résultat :
(2010, 20000, 12000) 18000.0
Exercice 6 : POO
Nous avons la classe grille suivante qui crée une grille de 5 x5.
from random import randint
class Grille:
def __init__(self):
self.matrix = [[randint(0,1) for i inrange(5) ] for j inrange(5)]
def afficher(self):
for ligne in self.matrix:
print(ligne)
Cette classe est composé de 2 méthodes :
__init__ pour créer la matrice (matrix);
afficher pour afficher la grille (matrix)
6.1. Tester la classe Grille avec les instructions suivantes :
magrille = Grille()
magrille.afficher()
6.2. Ecrire une méthode getXY(x,y) qui retourne la valeur de la case en position x et y de la grille.
Exemple :
print(magrille.getXY(2,3))
6.3. Ecrire une méthode setXY(x,y,valeur) qui change la valeur de la case en position x et y de la grille.
[[{"text":"Afin de vérifier vos acquis, répondez aux questions suivantes.","posi":1}],[{"text":"Avec le langage Python, comment je peux nommer une variable (plusieurs réponses sont possibles)?"},{"chekbox":[{"label":"monage=16","sol":true},{"label":"mon age=16","sol":false},{"label":"mon_age=16","sol":true},{"label":"1monage=16","sol":false},{"label":"monAge=16","sol":true},{"label":"monage1=16","sol":true}]}],[{"text":"On a écrit le script suivant :
var1 = 15.4
De quel type est la variable var1?
"},{"radio":[{"label":"int","sol":false},{"label":"float","sol":true},{"label":"string","sol":false}]}],[{"text":"On a écrit le script suivant :
var1 = 16
De quel type est la variable var1?
"},{"radio":[{"label":"int","sol":true},{"label":"float"},{"label":"string","sol":false}]}],[{"text":"On a écrit le script suivant :
var1 = \"Bienvenue en Nsi\"
De quel type est la variable var1?
"},{"radio":[{"label":"int","sol":false},{"label":"float"},{"label":"string","sol":true}]}],[{"text":"On a écrit le script suivant :
a = 5 / 2
print( a )
Quel résultat sera affiché?
"},{"radio":[{"label":"2.5","sol":true},{"label":"2"},{"label":"10","sol":false},{"label":"25","sol":false}]}],[{"text":"On a écrit le script suivant :
a = 5 // 2
print( a )
Quel résultat sera affiché?
"},{"radio":[{"label":"2.5","sol":false},{"label":"2","sol":true},{"label":"10","sol":false},{"label":"25","sol":false}]}],[{"text":"On a écrit le script suivant :
a = 5 * 2
print( a )
Quel résultat sera affiché?
"},{"radio":[{"label":"2.5","sol":false},{"label":"2"},{"label":"10","sol":true},{"label":"25","sol":false}]}],[{"text":"On a écrit le script suivant :
a = 5 ** 2
print( a )
Quel résultat sera affiché?
"},{"radio":[{"label":"2.5","sol":false},{"label":"2"},{"label":"10","sol":false},{"label":"25","sol":true}]}],[{"text":"On a écrit le script suivant :
a = 6
a = a + 1
a = a - 2
print( a )
Quel résultat sera affiché?
"},{"radio":[{"label":"4","sol":false},{"label":"5","sol":true},{"label":"6","sol":false},{"label":"7","sol":false}]}],[{"text":"On a écrit le script suivant :
a = \"bienvenue\"
b = \"en\"
c = \"Nsi\"
d = a+b+c
print( d )
Quel résultat sera affiché?
"},{"radio":[{"label":"bienvenueenNsi","sol":true},{"label":"bienvenue en Nsi"},{"label":"bienvenue","sol":false},{"label":"Nsienbienvenue","sol":false}]}],[{"text":"On a écrit le script suivant :
a = \"bienvenue\"
b = a[0]
print( b )
Quel résultat sera affiché?
"},{"radio":[{"label":"b","sol":true},{"label":"i"},{"label":"0","sol":false},{"label":"e","sol":false}]}],[{"text":"On a écrit le script suivant :
a = \"bienvenue\"
b = a[ 1 ]
print( b )
Quel résultat sera affiché?
"},{"radio":[{"label":"b","sol":false},{"label":"i","sol":true},{"label":"0","sol":false},{"label":"e","sol":false}]}],[{"text":"On a écrit le script suivant :
a = \"bienvenue\"
b = a[-1]
print( b )
Quel résultat sera affiché?
"},{"radio":[{"label":"b","sol":false},{"label":"i"},{"label":"0","sol":false},{"label":"e","sol":true}]}],[{"text":"On a écrit le script suivant :
import math
a = math.pi
print( a )
Quel résultat sera affiché?
"},{"radio":[{"label":"3.14159","sol":true},{"label":"PI"},{"label":"math.pi","sol":false},{"label":"3","sol":false}]}],[{"text":"On a écrit le script suivant :
import turtle
turtle.forward(50)
turtle.left(60)
turtle.forward(50)
turtle.left(60)
turtle.forward(50)
turtle.left(60)
Quelle est la figure dessinée?
"},{"radio":[{"label":"un triangle équilatéral","sol":true},{"label":"un triangle isocèl"},{"label":"un carré","sol":false},{"label":"un cercle","sol":false}]}],[{"text":"On a écrit le script suivant :
a=4
if a < 4 :
print(\"oui\")
else ;
print(\"non\")
Quel résultat sera affiché?
"},{"radio":[{"label":"oui","sol":false},{"label":"non","sol":true},{"label":"4","sol":false},{"label":"-4","sol":false}]}],[{"text":"On a écrit le script suivant :
a=input(\"Rentrer un nombre \")
# 3 est rentré par l'utilisateur
b = a + 2
print(b)
Quel résultat sera affiché?
"},{"radio":[{"label":"32","sol":false},{"label":"5"},{"label":"23","sol":false},{"label":"TypeError: can only concatenate str (not \"int\") to str","sol":true}]}],[{"text":"On a écrit le script suivant :
Considérons une activité : l'emprunt d’un livre dans une médiathèque municipale.
Alice trouve le livre qu’elle souhaite emprunter et approche d’une borne d'emprunt automatique. Elle scanne le code barre de sa carte de bibliothèque et des informations la concernant apparaissent :
« Alice, aucun livre en cours de prêt ».
Elle peut ensuite scanner le code barre apposé sur le livre. Les informations sont alors mises à jour pour afficher la liste des livres empruntés :
« La Programmation en pratique, B. Kernighan et R. Pike, Ed. Vuibert, 2017, ISBN 978-2711786701 ».
Sur la borne d’à côté, Basile vient rendre ses livres. Il scanne lui aussi sa carte et les informations le concernant apparaissent à l'écran :
« Basile, un livre en cours de prêt ».
Basile peut alors scanner le code barre du livre qu’il rapporte et ce dernier est supprimé de la liste.
Cette simple activité nous permet d'illustrer les caractéristiques d’un système d’information, c’est-à-dire d'un système technique (ici informatique) et humain permettant de gérer de l'information.
Que pouvons-nous dire sur le système informatique sous-jacent ?
En premier lieu, il contient une description d’objets (les livres), de personnes physiques (les usagers) et des
processus (l'emprunt et la restitution de livres).
Le système ne contient cependant qu’une «approximation» de la réalité :
En effet, il ne retient pour Alice et Basile que leur nom et prénom, éventuellement leur date d’inscription et
les livres en cours d'emprunt ;
Nous pourrions rajouter d’autres informations telle que :
- leur taille,
- la couleur de leurs yeux
- ou leur plat préféré ne sont pas connus du système.
De même pour les livres : seuls le titre, les auteurs, l'éditeur et l'ISBN (le numéro de série international unique attribué à chaque ouvrage publié) sont mémorisés. Le système ne retient donc que ce qui est nécessaire au bon fonctionnement des processus dont il facilite le déroulement : l'emprunt et le rendu de livres. Une telle « approximation » de la réalité s'appelle une modélisation.
Avant de préciser la façon de modéliser les données, finissons de passer en revue les différentes caractéristiques du système d’information de
la bibliothèque. Outre le stockage des données de chaque objet considéré (livres, usagers, emprunts, etc.), ce dernier doit garantir l'absence d’erreur.
Par exemple, si Basile repose son livre en rayon sans passer par une borne et qu’Alice tente de l’emprunter, le système doit détecter le problème et signaler que Basile a toujours le livre en sa possession.
De manière symétrique, si Alice rend un livre, alors le système doit effectivement garantir que quelqu'un d’autre peut l’emprunter.
Il y a un autre aspect que nous n'avons pas évoqué :
les documentalistes doivent pouvoir ajouter de nouveaux livres dans le système ou en retirer, par exemple quand ces derniers ont été perdus
ou sont en trop mauvais état.
Cependant, tout un chacun ne doit pas pouvoir modifier la liste des livres du système. Ce dernier doit donc permettre différents niveaux d'accès aux données. Enfin, on souhaite que quelle que soit la borne utilisée, la procédure soit la même.
Les données doivent donc être les mêmes. Il semble donc raisonnable de penser que ces données sont
stockées dans un même endroit et que les bornes ne sont que des clients permettant de consulter et modifier ces données de manière contrôlée.
Ces caractéristiques, et bien d’autres, sont celles d’un système de gestion de base de données. Ce dernier est le composant logiciel principal autour duquel est construit le système d’information.
Nous reviendrons sur ces aspects tout au long des séquences suivantes.
"},{"text":""}],[{"text":"
De manière générale, un modèle de données est une représentation (mathématique ou informatique) de concepts que l’on souhaite étudier.
Les intérêts d’une telle modélisation sont nombreux.
Un modèle permet d’énoncer des propriétés de ces données en termes logiques.
Il permet aussi de programmer des processus réels complexes (par exemple, emprunter un livre) sous
forme d’opérations élémentaires sur les données.
L'un des modèles de données les plus populaires est le modèle relationnel.
Ce dernier a été défini en 1970 par l’informaticien Américain Edgar F. Codd (1923-2003), alors qu’il était employé par IBM.
Dans ce modèle, un objet modélisé (on parle d’entité) est représenté par un n-uplet de valeurs scalaires et les collections d'objets par des ensembles de n-uplets.
Par exemple, le livre emprunté par Alice peut être représenté par le quintuplet suivant :
(’La Programmation en pratique’, ’B. Kernighan et R. Pike’, 'Vuibert’, 2017, '978-2711786701')
Dans ce dernier, quatre composantes sont des chaînes de caractères (le titre, les auteurs, le nom de l’éditeur et l’ISBN) et une composante est un entier
(l'année d'édition).
L'ensemble des livres de la bibliothèque peut alors être
représenté par un ensemble Livre de tels n-uplets :
Livre = {
(’La Programmation en pratique’,?B. Kernighan et R. Pike?, ’Vuibert’,2017,978-2711786701?), ('NSI 1ère : 30 leçons et exercices’, ?T. Balabonski, J.-C. Filliâtre, S. Conchon et K. Nguyen’, Ellipses”, 2019, °978-2340033641°), (’Zadig et autres contes’,’Voltaire’,’Folio’, 1992, °978-2070384815°), .....
}
On appelle un tel ensemble une relation. Une autre relation est l’ensemble Emprunt des livres actuellement empruntés :
Emprunt = {
(’La Programmation en pratique”, ’Alice’,01/04/2020), (Le Bouclier Arverne”, ’Charlie’,15/05/2020), (’Le Guide du routard galactique”, ’Charlie’,08/04/2020),...
Chaque relation se conforme à un schéma. Ce dernier est une description qui indique pour chaque composante des n-uplets de la relation leur nom et
leur domaine.
On appelle une telle composante un attribut. Par exemple, les éléments de la relation Livre possèdent cinq attributs :
- titre : le titre du livre, une chaîne de caractères;
- auteur : le ou les auteurs du livre, une chaîne de caractères;
- éditeur : l’éditeur du livre, une chaîne de caractères
- année : l’année de publication du livre, un entier naturel;
- isbn : l'identifiant unique du livre, une chaîne de caractères.
Une manière plus compacte de noter un schéma pour une relation est la suivante :
Livre(titre String, auteur String, éditeur String, année Int, isbn String)
Une base de données est un ensemble de relations.
Par exemple, la base de données de la médiathèque peut être composée de trois relations :
Livre, Emprunt et Usager.
Cette dernière, que nous n'avons pas encore décrite, recense toutes les personnes inscrites à la médiathèque.
Dans une telle relation, on stocke les noms et prénoms des personnes ainsi que la suite de caractères que représente le code barre de leur carte.
En effet, cette information est nécessaire pour retrouver la personne à partir de son code barre imprimé sur sa carte.
Par extension, on appelle schéma d’une base
de données l’ensemble des schémas des relations constituant la base.
","title":"Le modèle relationnel"},{"edit":"
Mettre le résultat ici (code et figure).
"}],[{"text":"
Modèle de données, structure de données.
Ces deux concepts présents en informatique se placent à différents niveau d’abstraction. Le modèle de données indique quelles caractéristiques d’une entités réelle on souhaite manipuler dans un programme.
Il indique aussi les relations des entités entre elles.
Par exemple, une personne peut emprunter plusieurs livres, mais un livre ne peut être emprunté que par au plus une personne.
Une structure de données indique la manière dont on va organiser les données en machine (dans un langage de programmation). Pour un même modèle de données (par exemple, un ensemble de noms) on peut choisir
plusieurs structures de données différentes (un tableau, une liste chaînée, un arbre binaire, . .….).
","title":"Modèle de données, structure de données"},{"edit":"
Mettre le résultat ici (code et figure).
"}],[{"text":"
Principes généraux
La théorie et la pratique des bases de données relationnelles est un domaine de recherche toujours actif, mais qui s’est fortement développé et complexifié depuis ses balbutiements dans les années 1970.
Nous ne proposons pas d'étudier ici cette théorie, qui irait bien au-delà du programme de terminale et demande des connaissances en mathématiques et en logique, notamment en théorie des ensembles.
Nous allons cependant essayer de donner les grands principes sous-jacents en les illustrant par des exemples.
Le fil conducteur de cette section sera l'exemple de la médiathèque que nous allons raffiner au fur et à mesure. La modélisation des données se décompose en plusieurs étapes :
1. déterminer les entités (objets, actions, personnes, etc.) que l’on sou-
********
2. modéliser les ensembles d’entités comme des relations en donnant leur schéma, en s’attachant en particulier à choisir le bon domaine pour chaque attribut ;
3. définir les contraintes de la base de données, c’est-à-dire l’ensemble des propriétés logiques que nos données doivent vérifier à tout moment.
","title":"Modélisation relationnelle des données"},{"edit":"
"}],[{"text":"
D'autres modèles. Il existe d’autres modèles que le modèle relationnel. En effet, ce dernier est parfois trop « rigide » pour représenter certains types de données. Ainsi, pour représenter des documents ayant une structure complexe (chapitres, sections, sous-sections, titres, texte libre, mise en gras ou en italique, tables, images, etc.) le modèle relationnel basé sur des ensembles de n-uplets est peu adapté.
On pourra faire appel dans ce cas à des modèles dits semi-structurés.
Un autre modèle de données est le modèle de graphe. Par exemple, si l’on souhaite représenter un réseau
social (une personne peut avoir des amis qui eux-mêmes peuvent avoir des amis, etc.) et effectuer dessus des traitements comme : « trouver tous les amis de mes amis qui ont les même centres d’intérêts que moi » alors le modèle relationnel est là encore peu adapté.
Faire cohabiter ces différents modèles dans un même système d’information permet d’effectuer des traitements riches, mais complexifie énormément la conception du système.
","title":""},{"edit":"
Mettre le résultat ici (code et figure).
"}],[{"text":"
Une contrainte d’intégrité est une propriété logique, préservée à tout instant par la base de données et qui garantit la cohérence des données.
En d’autres termes, une contrainte d'intégrité est un invariant de la base de données.
On distingue quatre grandes catégories de contraintes d’intégrité jouant des rôles complémentaires.
","title":"Contraintes d'intégrité"},{"edit":"
"}],[{"text":"
Considérons en premier lieu la relation des usagers, que l’on va rendre un peu plus réaliste.
Normalement, lorsqu'une personne s'inscrit dans une médiathèque, elle donne non seulement son nom et
son prénom, mais aussi son adresse.
Cette dernière peut servir à déterminer son tarif d'abonnement (par exemple, les gens qui habitent dans la ville de la médiathèque ont un tarif préférentiel) ou à envoyer des courriers de relance.
De nos jours, il est aussi commun de stocker une adresse e-mail. Lors de l'inscription, la personne obtiendra une carte avec un code barre et ce dernier doit donc également être associé à l'usager.
Chacune des propriétés que l'on souhaite renseigner pour un usager correspondant à un attribut pour la relation Usager
.
Il peut être compliqué de décider si une propriété complexe (par exemple l'adresse) doit être séparée en plusieurs attributs.
Ce découpage est important car il va influencer la façon dont on va interroger les données stockées.
On peut s'étonner ici que les attributs soient tous représentés par des chaînes de caractères.
Le code postal (attribut cp) et le code barre (attribut
code_barre) sont a priori des suites de chiffres.
On pourrait donc vouloir les représenter par des entiers.
Le problème est que ces suites de chiffres peuvent commencer par le chiffre 0, non significatif pour les entiers. Ainsi, le code postal de la ville d’'Oyonnax est 01100. Le stocker comme un entier reviendrait à stocker l’entier 1100, ce qui peut être problématique par la suite.
Dans un premier temps, nous allons nous abstraire lors de la modélisation des aspects techniques propres aux systèmes de gestion de bases de données et au langage SQL.
Nous proposons donc d’utiliser des domaines génériques, inspirés des types de données des langages de programmation :
- String représente les chaînes de caractères
- Int représente les entiers signés (qu’on suppose de taille arbitraire, comme l es entiers de Python)
- Boolean représente les valeurs booléennes True et False
- Float représente les nombres flottants.
- Date représente des dates (jour/mois/année)
- Time représente des instants (heure : minute : seconde)
Le choix des domaines est particulièrement important, car il doit répondre à deux impératifs :
- permettre de représenter exactement et sans perte d’information toutes les valeurs possibles pour un attribut ;
- limiter autant que possible la saisie de valeurs illégales ou mal formées
Les contraintes de domaines sont toutes les propriétés que le domaine d’un attribut va permettre de garantir.
Par exemple, si l’on souhaite représenter un choix binaire (abonné/non abonné, présent/absent, etc.) le domaine Boolean ne contenant que deux valeurs permet de garantir la contrainte que l’attribut ne peut pas avoir de troisième valeur.
De même, si l’on souhaite stocker l’âge d'une personne, le domaine Int est plus approprié que String, ce dernier permettant de représenter des valeurs qui ne sont clairement pas des âges (par exemple 'Toto').
En revanche, l'utilisation seule du domaine Int ne permet pas de garantir l’absence de données incohérentes dans la base. Ainsi, des valeurs telles que -10 ou 45000 sont des entiers valides mais è priori pas des ages raisonnable pour des personne.
","title":"Contrainte de domaine"},{"edit":"
"}],[{"text":"
La contrainte d’entité permet de s’assurer que chaque
élément d’une relation est unique et identifie une entité de manière non ambiguë.
Considérons une relation Usager où l’on n'aurait stocké que les noms et prénoms des personnes.
Il est relativement courant que deux personnes portent le même nom de famille et le même prénom, surtout dans des villes assez peuplées.
Si ces deux personnes s'inscrivent toutes les deux à la médiathèque, la relation Usager contiendra deux couples identiques, ce qui poserait problème.
Pour éviter cette situation, il faut s'assurer, lors de la modélisation, que pour chaque entité d’une relation, un attribut ou en ensemble d’attributs permet d'identifier cette entité de manière unique.
La relation Usager définie à la section précédente respecte bien cette contrainte. En effet, le code barre est unique (car la carte associée est donnée à la personne au moment où elle s'inscrit et on peut supposer que le système ne réutilise pas un numéro déjà attribué).
On appelle un tel ensemble d’attributs la clé
primaire de la relation.
On indique cela en les soulignant dans le schéma :
Notons que les attributs pouvant jouer le rôle de clé primaire peuvent ne pas être uniques. Dans notre cas, on pourrait imaginer que l’adresse email puisse aussi jouer le rôle de clé primaire. Ou encore, la combinaison du nom, du prénom, de l’adresse, du code postal (attribut cp) et de la ville ensembles jouent le rôle de clé primaire.
Mais attention, le choix de la clé primaire est important, car le système garantira son unicité (pour préserver la contrainte d’entité). En fonction des choix, certains cas d’utilisation seront impossibles.
Par exemple, si l’on choisit l’adresse email, alors il ne sera pas possible pour un parent de renseigner son adresse e-mail dans le compte de ses enfants l’adresse devant être unique pour chaque entité de la relation.
De même, si on choisit la combinaison des noms, prénoms, adresses, code postal et ville, il faut s’assurer que l’adresse est suffisamment précise pour distinguer deux personnes homonymes qui habiteraient le même immeuble de la même ville.
Dans le cadre de la relation Livre, on peut redonner le schéma initial en utilisant l'ISBN comme clé primaire :
Livre(titre String, auteur String, éditeur String, année Int, isbn String)
Encore une fois, cette modélisation est raisonnable.
Utiliser uniquement le titre ou le couple titre et auteurs comme clé primaire aurait posé problème si la bibliothèque avait en stock le même livre dans deux versions différentes (par exemple en livre de poche et en grand format illustré).
L’isbn lui-même peut ne pas être suffisant si la médiathèque dispose de plusieurs copies du même ouvrage. Il faudrait alors distinguer ces différentes copies dans la base de données, par exemple en ajoutant un identifiant unique sur chaque exemplaire pour servir de clé (par exemple un code barre unique ajouté par la médiathèque).
","title":"Contrainte d'entité"},{"edit":"
Mettre le résultat ici (code et figure).
"}],[{"text":"
Les clés primaires ne permettent pas seulement
de distinguer les entités de manière unique.
Elles permettent aussi de servir de référence dans une autre relation.
Intéressons nous maintenant à la relation Emprunt. L'exemple que l’on a donné en introduction est naïf, car
il stocke dans cette table le prénom de l'utilisateur, le titre du livre et la date de rendu.
On pourra plutôt définir la relation Emprunt avec le schéma suivant :
Emprunt(code_barre ; String, isbn : String, retour : Date)
Ici, code_barre et isbn sont des clés étrangères.
Cela signifie que la valeur de l’attribut code _barre dans la relation Emprunt doit être l’une des valeurs existantes pour cet attribut dans la relation Usager.
De même, isbn doit correspondre à un isbn existant dans la relation Livre. Cette contrainte permet de garantir que la relation Emprunt ne mentionne que des livres et des usagers connus de la base de données.
Elle permet d'éviter de rajouter des valeurs fictives ne correspondant à aucun utilisateur ou à aucun livre.
De manière plus importante, elle empêche aussi de supprimer des entités des relations Livre et Usager. En particulier, si la relation Emprunt contient l’entité
(\"123456789\", \"978-2340033641\", 07/03/2020),
alors retirer l’utilisateur dont le code barre est \"23456789\" de la relation Usager est une violation de la contrainte de référence, car la valeur \"123456789\" ne
serait plus celle d’une clé primaire dans la relation Usager (et d'un point de vue pratique, cela permet de s'assurer qu'avant de désinscrire une personne, cette dernière a rendu tous ses livres).
Une autre remarque que l’on peut faire sur le schéma Emprunt est que l’isbn est déclaré comme clé primaire de la relation. Cela implique qu’un même livre ne peut apparaître dans deux entités distinctes. Là encore, on
voit que la contrainte nous permet de garantir la cohérence des données :
il n’est pas possible qu’un même livre soit emprunté par deux usagers en même temps.
Une dernière observation importante sur l’utilisation des clés étrangères est qu'elle permet de créer des associations multiples entre entités de différentes relations.
Ici, on peut associer au même utilisateur plusieurs livres
en cours d'emprunt. En effet, code_barre n'étant qu’une clé étrangère, il n’a pas à être unique dans la relation Emprunt :
un même utilisateur a le droit d’emprunter plusieurs livres. En d’autres termes, nous pouvons par ce moyen associer à un utilisateur une liste de livres.
Nous pouvons utiliser cette technique pour pallier un défaut de conception de la relation Livre.
En effet, dans cette dernière, nous avons représenté les auteurs du livre comme un chaîne de caractères dans laquelle est écrite la liste des auteurs.
Bien que cette modélisation fonctionne, elle ne permet pas d'exprimer certaines contraintes sur les données, par exemple qu’un même auteur n’apparaît pas deux fois pour le même livre.
En effet. les chaînes de caractères étant arbitraires, on peut y saisir n'importe quoi. L'utilisation de clés étrangères va nous permettre de remédier à ce problème. On simplifie dans un premier temps le schéma de la relation Livre pour ne plus mentionner les auteurs :
La seule contrainte sur les identifiants est qu’ils doivent être uniques.
Il n’y a en particulier aucune notion d'ordre. Munis des relations Livre et Auteur, nous pouvons associer des auteurs à des livres au moyen de la relation Auteur_ de :
Auteur_de(a_id : Int, isbn : String)
Cette relation associe des auteurs à des livres. Les attributs a_id et isbn sont des clés étrangères faisant référence aux relations Auteur et Livre respectivement.
Le couple de ces deux attributs forment la clé primaire de la table Auteur_de. Cela empêche qu’un même auteur et un même livre apparaissent deux fois dans la relation et donc qu’un même auteur soit mentionné deux fois pour le même ouvrage.
En revanche, rien n'empêche qu’un même auteur apparaisse plusieurs fois pour des ouvrages différents, ou que différents auteurs apparaissent pour le même ouvrage.
Attention cependant, si un isbn n'apparaît pas dans cette relation, c’est que le livre correspondant n’a pas
d'auteur. Les contraintes de clé primaires et étrangères ne permettent pas d'empêcher ce cas de figure.
","title":"Contrainte de référence."},{"edit":"
"}],[{"text":"
Nous terminons ce tour d’horizon des contraintes
d’intégrité par les contraintes utilisateurs (parfois appelées contraintes métier).
Ces dernières sont toutes les contraintes d’une relation qu’on ne peut exprimer par les trois précédentes. Un exemple de contrainte utilisateur est qu’un âge de personne doit être positif et inférieur à 200. Pour notre médiathèque, une autre contrainte utilisateur pourrait être que la chaîne de caractères représentant l'e-mail contienne un et un seul caractère « @ ».
Ces contraintes, liées à l’utilisation que l’on veut faire de la base de données,sont importantes mais difficilement exprimables dans la syntaxe très simple
des schémas.
On veillera donc à en faire une description précise en français.
Nous verrons dans la séquence suivant que les systèmes de gestion de bases de données proposent une syntaxe pour écrire certaines de ces contraintes.
","title":"Contraintes utilisateurs"},{"edit":"
"}],[{"text":"
Le modèle relationnel est un modèle dans lequel
les données sont représentées par des ensembles de n-uplets appelés des relations.
Un élément d’une relation est appelé une entité. Il représente généralement un objet, une action, une personne du monde réel.
Chaque entité possède des propriétés appelées des attributs.
On spécifie une relation en donnant son schéma, c’est-à-dire son nom, la liste de ses attributs avec leur domaine, c’est-à-dire l’ensemble des valeurs que peuvent prendre un attribut.
Une base de données est un ensemble de relations et le schéma d’une base est l’ensemble des schémas des
relations qui la compose.
La cohérence des données au sein d’une base est assurée par des contraintes d’intégrité.
Ces dernières sont des invariants, c’est-à-dire des propriétés logiques que les données doivent vérifier à tout instant.
On distingue parmi ces contraintes :
Les contraintes d’entité qui garantissent que chaque entité d’une relation est unique.
Une clé primaire est un ensemble d’attributs qui identifie chaque entité de la relation de manière unique et garantit la contrainte d’entité.
Les contraintes de référence qui créent des associations entre deux relations. Elle permettent de garantir qu’une entité d’une relation B mentionne une entité existante dans une relation A.
Une clé étrangère est un ensemble d’attributs d’une table qui sont une clé primaire dans une autre table.
Les contraintes de domaines qui restreignent les valeurs d’un attribut à celles du domaine et évitent que l’on puisse donner à un attribut une valeur illégale.
Les contraintes utilisateurs qui restreignent encore plus les valeurs d’un ou de plusieurs attributs et sont guidées par la nature des données que l’on souhaite stocker dans la base.
Toutes ces contraintes doivent être utilisées pour assurer la qualité des données :
elles permettent de s'assurer que les données sont « conformes » aux entités du monde réel qu’elles représentent.
","title":" Conclusion"},{"edit":"
Mettre le résultat ici.
"}],[{"text":"
On souhaite modéliser un annuaire téléphonique simple dans lequel chaque personne (identifiée par son nom et son prénom) est associée
à son numéro de téléphone.
Proposer une modélisation relationnelle de cet
annuaire.
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
De façon très simple, on peut modéliser l'annuaire
de la manière suivante :
Annuaire(nom String, prénom String, tel String)
On n’oubliera pas de préciser que le numéro, par définition unique, est une clé primaire. Son domaine peut être String afin d’éviter les problèmes de 0 en première position ou de permettre de saisir des caractères non numériques
comme +.
"}],[{"text":"
Donner la modélisation relationnelle d’un bulletin scolaire.
Cette dernière doit permettre de mentionner :
- des élèves, possédants un numéro d'étudiant alphanumérique unique;
- un ensemble de matières fixées, mais qui ne sont pas données;
- au plus une note sur 20, par matière et par élève.
On prendra soin de préciser toutes les contraintes utilisateurs qui ne peuvent êtres inscrites dans les schémas des relations.
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
La modélisation consiste en trois relations
Eleve(nom String, prénom String, num String)
Matiere(intitule String, m_ind INT)
Note(num String, m_id INT, note Float)
On n’a pas moyen de forcer que la valeur de la note soit comprise entre 0 et
20. On à fait le choix de donner un indentifiant numérique à la matière.
"}],[{"text":"
On considère la solution donnée pour l'exercice précédent sur l'annuaire.
Dire si chacun des ensembles est une relation valide pour le schéma Annuaire.
Annuaire(nom : String, prénom : String, tel :String)
2. Oui, la relation ne contient qu’un triplet bien formé
3. Non, les deux triplets ont la même valeur pour l’attribut tel qui est une clé primaire.
4. Oui, les deux clés primaires des deux entités sont différentes.
5. Non, l’ensemble contient un couple, qui n’est pas une entité bien formée pour le schéma Annuaire
6. Non, l’ensemble contient un triplet dont la clé primaire est un nombre et non pas une chaîne
"}],[{"text":"
On considère la solution donnée pour l'exercice sur les bulletins scolaires. Dire si chacun des ensembles est une relation valide pour le schéma de la base de données du bulletin de notes,
1 - Eleve={}
- Matiere = {}
- Note={}
2. - Eleve= {(’Titi’,’Toto',’AB56789'), }
- Matiere = {('NSI’,0), (’Sport',1)}
- Note = {(’AB56789',1,17)}
3 - Eleve= {('Titi','Toto','AB56789'),}
- Matiere = {('NS1',0)}
- Note = {('AB56789',1,17)}
4. - Eleve= {('Titi','Toto','AB56789'), }
- Matiere = {('NSI',0),}
- Note = {('AB56789',0, 17), ('AB56789',0, 18)}
5. - Eleve= {('Titi','Toto','AB56789'),}
- Matiere = {('NSI',0),('Sport',1)}
- Note = {('AB56789',0, 17), ('AB56789',1,17)}
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
1. Oui, les relation vides sont des ensembles valides
2. Oui, les données sont cohérentes
3. Non, l'identifiant 1 n’est pas une clé primaire dans Matiere
4. Non, il y a deux triplets avec la même clé primaire composite ('AB56789',0) dans Note
5. Oui, la note n'étant pas à la clé primaire de Note on peut avoir deux
entités avec la même note.
"}],[{"text":"
Modéliser des informations sur les départements français.
Pour chaque département on veut pouvoir stocker son nom, son code, son chef-lieu et la liste de tous les départements voisins.
Attention, les codes de département sont tous des nombres, sauf la Corse du Sud et la Haute Corse
qui ont les codes 2A et 2B respectivement,
Les départements d'Outre-Mer ont un code sur trois chiffres (de 971 à 976).
Proposer une contrainte utilisateur permettant d'éviter la redondance d'information dans la liste des
La relation Voisin stocke les couples (d1 , d2) des code de deux départements :
si d1 est voisin de d2.
Pour éviter de stocker aussi (d2, d1), on peut rajouter
la contrainte que le code de d1 est plus petit que celui de d2 (au sens de la comparaison des chaînes de caractères).
Par exemple on stockera (91,92) pour indiquer que l'Essonne et les Hauts-de-Seine sont voisins, mais pas
(92,91).
"}],[{"text":"Proposer une modélisation pour un réseau de bus.
Cette dernière doit être suffisamment riche pour permettre de générer, pour chaque arrêt de bus du réseau, une fiche horaire avec tous les horaires de passage de toutes les lignes de bus qui desservent l'arrêt.
Indication : ici, plus qu'une simple traduction du français vers le modèle relationnel, on essayera de déterminer dans un premier temps quelles informations sont pertinentes et comment les représenter. On pourra ensuite procéder à la modélisation sous forme de relations.
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
Considérons dans un premier temps les informations
pertinentes :
- Le réseau est constitué d’arrêts de bus. Un arrêt peut être représenté par ses coordonnées GPS (latitude et longitude, deux nombres à virgule), son nom et un identifiant unique.
- Une ligne de bus est représenté par un numéro unique et un nom (le nom est juste une chaîne de caractères permettant d'identifier la ligne simplement, comme ’Centre-Ville - Aéroport”)
- Les horaires de passage. Pour chaque ligne et chaque arrêt desservi par cette ligne, on stocke la liste des horaires de passage, ainsi qu’une indication sur le jour de validité de l’horaire. Un horaire peut être valide en semaine, le samedi ou le dimanche/jour férié.
Une fois faite cette analyse, on peut proposer la modélisation en trois relations
Arret(lat Float, lon Float, nom String, a_id Int)
Ligne(num Int, nom String)
Horaire(num Int, a_id Int, heure Time, jour String)
On remarque qu’un horaire de passage est identifié de manière unique par a_id Int, heure et jour.
"}],[{"text":"
On considère deux relations R(a Int,b Int,c Int) et
S(a Int,e Int) où l’attribut a de S est une clé étrangère faisant référence à a de R. Dire si les affirmations suivantes sont vraies ou fausses, en justifiant.
1. Les a de R sont tous deux à deux distincts.
2. Les b de R sont tous deux à deux distincts.
3. Les a de S sont tous deux à deux distincts.
4. Les e de S sont tous deux à deux distincts.
5. S peut être vide alors que R est non vide.
6. R peut être vide alors que S est non vide
","title":"Exercice"},{"edit":"
Mettre le résultat ici (code et figure).
"},{"solution":"
1. Vrai. L’attribut a étant une clé primaire, tous les a sont distincts dans R.
2. Faux. L’attribut b n'intervient pas dans la clé. Ses valeurs peuvent se répéter, par exemple l’ensemble {(1,2, 3), (2,2,3)} est une instance valide du schéma R.
3. Faux. L’attribut a de S, considéré seul, est une clé étrangère. La seule contrainte est qu’il doit avoir la même valeur que la valeur d’un a de R.
4. Faux. L'attribut e seul n’a pas de contrainte. Il participe à la clé primaire de $, il faut donc que les couples (a,e) soient deux à deux distinct.
5. Vrai. Si S est vide, elle respecte bien la propriété que chaque valeur de a de S (il n’y en à pas) référence une valeur de a dans R.
6. Faux. Si S est non vide, c’est qu’elle contient au moins une entité. L’attribut a pour cet entité doit forcément référencer un a existant dans R qui ne peut donc pas être vide.
En poursuivant votre navigation sur mon site,
vous acceptez l’utilisation des Cookies et autres traceurs
pour réaliser des statistiques de visites et enregistrer
sur votre machine vos activités pédagogiques.En savoir plus.