13.1- TP - Piles et files
Ce TP a pour but de vous faire travailler les implémentations de file, avec une proposition d'application.
Commencez par exécuter la cellule suivante :
I. Rappels
Rappel : les piles
Pour rappel, les piles, ou Last In First Out, sont une structure de données semblable à une pile d'assiettes, avec les opérations suivantes :
- empiler (un élément sur la pile)
- dépiler (un élément de la pile)
- optionellement : regarder l'élément du haut de la pile, connaitre la taille de la pile ...
En python, cela correspond au type list, avec ses méthode L.append(x) et L.pop().
Les files
Les files, ou FIFO, First In First Out, sont une structure de données semblable à une file d'attente, avec les opérations suivantes :
- enfiler (ajouter un élément au début de la file)
- défiler (enlever l'élément de la fin de la file)
- optionellement : regarder l'élément de la fin de la file, connaitre la taille de la pile ...
On va s'intérésser à trois implémentations possibles des files.
II. Implémentation naïve
On va commencer par faire une implémentation dite naïve des files, en utilisant des listes. On va ajouter les éléments à la fin de la liste et les enlever au début.
Question 1
Complétez les fonctions suivantes de la manière la plus simple possible, sans se soucier de la complexité des opérations.
Loading editor...
def file_vide():
"""créé et renvoie une file vide"""
#à compléter
def enfiler(file, x):
"""enfile l'élément 'x' à la file 'file' """
#à compléter
def defiler(file):
"""defile et renvoie un élément de la file"""
#à compléterLoading editor...
III. Application
On propose l'application suivante : on a une imprimante, et un certain nombre de terminaux (téléphones, ordinateurs) connectés à cet imprimante.
Pour simplifier, on suppose qu'une impression met une seconde. Par contre, les demandes d'impression par les terminaux peuvent arriver à n'importe quelle fréquence !
Question 2
Complétez ces deux fonctions pour faire fonctionner le serveur d'impression de manière cohérente. Vous pouvez ensuite exécuter la cellule await lancer_imprimante() pour simuler l'exécution avec vos fonctions : les demandes et les impressions s'afficheront au fur et à mesure sous la cellule.
La fonction prochain_document peut renvoyer None s'il n'y a rien à imprimer.
Loading editor...
file = #à compléter
def prochain_document():
#à compléter
def demande_impression(document):
#à compléterLoading editor...
await lancer_imprimante()
Loading editor...
IV. Implémentation efficace des files
Tant qu'on a 10 documents à imprimer, peu importe si notre file est efficace ou non. Mais maintenant, imaginons que vous gérer l'ensemble des tâches d'un ordinateur... Il y en a possiblement des milliers, à gérer chaque miliseconde ! Dans ce cas, on a besoin d'implémentations plus efficaces.
On va ici en proposer deux.
1. La double pile
Question 3
Supposons qu'on aie une variable pile contenant une pile (sous la forme d'une liste python).
Que se passe-il si on prend les éléments de la pile, un à un, et qu'on les transfert dans une autre pile ? Pour rappel, on n'utilise seulement les opérations .append et .pop.
Et non ! Les éléments sont-ils vraiment dans le même ordre ?
En effet : les éléments d'en haut se retrouvent en bas, et vice-versa.
Question 4
Écrivez une fonction transfert qui prend en argument une pile pile (sous forme de liste python), et une liste vide resultat, et qui transfert les éléments de pile dans resultat avec uniquement append et pop. A la fin de votre fonction, resultat doit contenir pile inversé, et pile doit être vide.
Testez-là sur la liste [1, 2, 3, 4, 5].
Loading editor...
def transfert(pile, resultat):
"""Transfere les élémentes de "pile" à "résultat" """
#à compléterOn va maintenant utiliser cette propriété pour implémenter une file. L'idée est d'avoir une pile "d'entrée" et une pile de "sortie". Notre "file" sera donc constituée de deux piles !
Essayez : enfilez quelques éléments, puis défilez, et observez le transfert d'une pile à l'autre.
L'idée ici, est d'ajouter tous les éléments dans la pile d'entrée, avec un simple append. Mais lorsqu'on veut sortir un élément, on ne peux pas utiliser pop sur la même pile, sinon on sortirait les éléments à l'envers, et on aurait, justement, une pile (et pas une file !).
On va donc placer, en inversé, toute la pile d'entrée dans la pile de sortie. Les éléments de cette dernière seront alors dans le bon ordre pour être pop directement !
Attention, pour que l'ordre soit préservé, il faut respecter une règle : on ne transfère la pile d'entrée dans la pile de sortie que si la pile de sortie est vide.
Question 5
Effectuez la séquence d'opération suivante sur papier, en dessinant l'état des deux piles au fur et à mesure de l'execution :
- enfile 1
- enfile 2
- enfile 3
- defile
- defile
- enfile 4
- defile
- defile
- enfile 5
On va utiliser une liste de deux listes pour implémenter notre file en python : file = [[3, 4, 5], [2, 1]].
Question 6
Écrivez une fonction python file_vide2 pour créer une nouvelle file vide.
Loading editor...
Question 7
Écriver une fonction enfile2 qui prend en argument une file et un élément, et qui y enfile l'élément.
Loading editor...
Question 8
Écriver une fonction defile2 qui prend en argument une file (donc une paire de listes), et qui y défile un élément et le renvoie. Vous pouvez utiliser de votre fonction de la question 4.
Loading editor...
Question 9
Modifiez l'exemple de l'imprimante pour utiliser vos nouvelles fonctions, et vérifiez que l'ordre est bien le bon.
Loading editor...
2. La liste circulaire
L'autre manière classique d'implémenter une file, est d'utiliser une "liste circulaire".
L'idée est d'avoir deux pointeurs (des indices de listes), pour le début et la fin, qui se déplacent quand on ajoute ou retire des éléments. Nos éléments sont stockés dans une liste python de taille fixe, et quand on arrive au bout, on revient au début.
Voici une petite animation du fonctionnement :
file vide
Il est important, pour cette implémentation, que la file ne "déborde" pas : si le pointeur début rattrape le pointeur fin, on doit renvoyer une erreur. Dans le cadre de ce TP, vous pourrez supposer que ce cas n'arrive jamais.
En python, on va prendre un type de file qui est un triplet (liste de 3 éléments) [liste, debut, fin].
Dans le dessin, liste est la série de 7 carrés. Ces carrés (les éléments de la liste) peuvent être soit pleins (et contenir un nombre), soit vides (les carrés blanc, que vous pouvez remplir par exemple par None).
debut et fin sont des indices de cette liste, représentés dans le dessin par les flèches correspondantes.
Question 10
Écrivez une fonction cree_file3(n) qui créé une nouvelle file, vide, avec une capacité (donc taille maximale) de n.
Loading editor...
Question 11
Écrivez une fonction enfile3(file, x) qui ajoute un élément à la file, ou renvoie une erreur si celle-ci est pleine (en utilisant assert par exemple).
Loading editor...
Question 12
Écrivez une fonction defile3(file) qui défile un élément de la file, et renvoie une erreur si celle-ci est vide.
Loading editor...
Question 13
Testez à nouveau vos fonctions sur l'exemple de l'imprimante.
Loading editor...
Entrez le code donné en cours pour accéder à la solution :