Le Service des sports fait appel à vous dans le cadre du développement d'un système informatique pour la gestion des résultats de la course des 24h Vélo de Louvain-la-Neuve. Vous devez réaliser un programme Python permettant d'encoder et de manipuler les différentes données nécessaires (coureurs, résultats, classements) pour tenir à jour les classements en cours de course. Par exemple, il faut pouvoir afficher en temps réel, sur des écrans disposés dans la ville et sur internet, le classement des meilleurs temps pour le tour du circuit.
Pour cette mission, vous allez devoir implémenter une classe Python permettant de manipuler un tel classement et de le mettre à jour avec de nouveaux résultats. Le classement doit être ordonné, et on ne désire pas refaire le tri pour chaque nouveau résultat: il faut donc une structure de liste ordonnée avec des opérations qui maintiennent cet ordre. Vous utiliserez pour cela une liste chaînée.
A l'issue de cette mission, chacun d'entre vous :
Comment bien préparer la mission?
Assister au cours pour comprendre les notions et concepts qui seront abordés.
Lire attentivement et comprendre le chapitre sur Linked lists dans la partie Objects du syllabus théorie :
- 6 - Linked lists
- Embedded references
- The Node class
- Linked lists as collections
- Linked lists and recursion
- Infinite lists
- Ambiguity between lists and nodes
- Modifying lists
- Wrappers and helpers
- The LinkedList class
Essayer et comprendre le code développé dans le syllabus et qui se trouve aussi dans l'annexe correspondant :
- Appendix - Source code of linked lists
- LinkedList class
- Node class
- Creating and using LinkedList objects
Dans cette mission, vous devrez de nouveau utiliser le framework unittest pour produire des classes de test. Relisez la documentation si vous aviez du mal à créer des tests unitaires lors de la mission précédente.
Répondre aux questions à choix multiples et aux autres questions de démarrage.
Ces questions supposent que vous avez lu le chapitre du syllabus théorique sur les listes chaînées et que vous avez lu et testé le code des classes LinkedList et Node dans l'annexe code des classes LinkedList et Node dans l'annexe correspondante.
Les questions à choix multiples de cette mission sont accessibles en ligne depuis https://inginious.info.ucl.ac.be/course/LSINF1101-PYTHON/Session11_QCM.
Répondez aux questions suivantes:
Question 1a : Manipuler une liste vide
Cette question concerne l'utilisation de la classe LinkedList dont le code se trouve dans l'annexe suivante du syllabus : Appendix - Source code of linked lists
Utilisez la classe LinkedList pour créer une liste chaînée vide l.
Donnez une instruction Python pour imprimer le contenu de cette liste l.
Quel résultat sera affiché si on essaie d'imprimer la liste l avec l'instruction print(l)? Expliquez.
Donnez une instruction Python pour imprimer la taille de cette liste l .
Quel résultat sera affiché si on essaie d'imprimer la taille avec l'instruction print(l.__length)? Expliquez.
Question 1b : Remplir la liste
Pour ajouter des éléments à une liste vide, quelqu'un a écrit les instructions suivantes:
l = LinkedList() l.add(3) l.add(2) l.add(1) l.print()
Mais quelqu'un d'autre à écrit:
l = LinkedList() l.add(Node(3)) l.add(Node(2)) l.add(Node(1)) l.print()
Dans les deux cas, exécuter l'instruction l.print() affichera [ 1 2 3 ].
Lequel des deux fragments de code ci-dessus vous semble correct? Pourquoi l'autre imprime quand-même le même résulat?
Indice : qu'est-ce qui se passe si on exécute l'instruction print(Node(Node(Node(1))))? Pouvez-vous expliquer?
Dans le code donné dans l'annexe Appendix - Source code of linked lists la méthode print() de la classe LinkedList imprime les éléments d'une liste chaînée, séparés par une espace. Par exemple:
>>> l = LinkedList() >>> l.add(3) >>> l.add(2) >>> l.add(1) >>> l.print() [ 1 2 3 ]
Or, normalement la convention est de séparer les éléments d'une liste par des virgules. Ajoutez une nouvelle méthode print_avec_virgule() dans la classe LinkedList qui imprime la liste comme suite:
>>> l.print_avec_virgule() [ 1, 2, 3 ]
Attention: il n'y a pas de virgule après le dernier élément ou si on imprime une liste vide.
>>> l = LinkedList() >>> l.print_avec_virgule() [ ]
Généralisez la méthode print_avec_virgule de l'exercice précédente par une méthode print_avec_separateur qui peut prendre n'importe quelle séparateur. Par exemple:
>>> l = LinkedList() >>> l.add(3) >>> l.add(2) >>> l.add(1) >>> l.print_avec_separateur(", ") [ 1, 2, 3 ] >>> l.print_avec_separateur(" ") [ 1 2 3 ] >>> l.print_avec_separateur(" - ") [ 1 - 2 - 3 ]
Ajoutez à la classe LinkedList une méthode add_to_end(self, cargo) pour ajouter un noeud en queue de la liste. Ajoutez d'abord un nouvel attribut last dans la méthode d'initialisation pour garder une référence vers ce dernier élément. Changez ensuite la méthode add pour assigner cette référence lors de l'ajout du premier noeud, et la méthode remove pour remettre cette référence à None lorsque la liste est de nouveau vide. Implémentez ensuite la méthode add_to_end.
Est-ce facile d'implémenter une méthode remove_from_end(self, cargo) pour supprimer un élément de la queue d'une LinkedList? Expliquez en mots comment vous feriez.
En Python il est possible d'imbriquer une classe dans une autre. Ceci peut être utile si une classe n'est utilise que par une seule autre classe. C'est le cas ici avec la classe Node. Ce n'est qu'une classe auxiliaire pour la classe LinkedList. Adapter le code de votre classe LinkedList en imbriquant le code de la classe Node dedans. Il suffit d'indenter le code de la classe Node et de changer chaque référence à la classe Node à l'interieur de la classe LinkedList par self.Node.
Quels sont les avantages et désavantages d'imbriquer la classe Node dans la classe LinkedList?
Considérez qu'on désire utiliser la classe LinkedList pour stocker une liste de strings. On souhaite qu'elle soit en permanence en ordre alphabétique croissant.
Dessinez complètement une telle liste chainée contenant les trois strings "abc", "def" et "xyz".
Expliquez en français et dessinez les opérations à effectuer pour ajouter un Node contenant "aaa" dans cette liste.
Même question avec un Node contenant "ghi".
Même question avec un Node contenant "def".
Même question avec un Node contenant "zzz".
Lors de la mission précédente, les tests unitaires avec unittest ont été introduits. Pour revisiter ce concept, consultez éventuellement un tutoriel online comme https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python/2235416-les-tests-unitaires-avec-unittest
Créez un test unitaire pour tester la classe LinkedList. Ce test doit contenir des tests pour les différentes méthodes de la classe LinkedList comme size(), first() ou add(valeur) (code original), remove() (question 5), add_to_end(valeur) (question 6) ou insert(valeur) (question 9). Pour vous mettre sur le bon chemin, votre classe test aura la structure suivante:
import unittest class LinkedListTest(unittest.TestCase): """Classe de test utilisé pour tester la classe LinkedList""" def test_size(self): """Test de la methode size() de la classe LinkedList.""" # ... assert*(...) ... def test_first(self): """Test de la methode first() de la classe LinkedList.""" # ... assert*(...) ... def test_add(self): """Test de la methode add(valeur) de la classe LinkedList.""" # ... assert*(...) ... def test_remove(self): """Test de la methode remove() de la classe LinkedList.""" # ... assert*(...) ... def test_add_to_end(self): """Test de la methode add_to_end(valeur) de la classe LinkedList.""" # ... assert*(...) ... def test_insert(self): """Test de la methode insert(valeur) de la classe LinkedList.""" # ... assert*(...) ... if __name__ == '__main__': unittest.main()