# TP : Simulateur de Navigateur Web — Piles et Files en action > **Thème** : Structures de données linéaires (Pile, File) --- ## Contexte Chaque jour, vous utilisez un navigateur web (Chrome, Firefox, Safari...). Avez-vous déjà remarqué comment fonctionnent les boutons **← Retour** et **→ Suivant** ? Et comment Netflix gère votre file d'attente "**À regarder ensuite**" ? Dans ce TP, vous allez recréer ces mécanismes en utilisant les structures **Pile** et **File**. --- ## Partie 1 : Implémentation des structures ### Exercice 1 : La classe Pile Implémentez une classe `Pile` avec les méthodes suivantes : ```python class Pile: """Structure de données LIFO (Last In First Out).""" def __init__(self): """Initialise une pile vide.""" pass def empile(self, element): """Ajoute un élément au sommet de la pile.""" pass def depile(self): """ Retire et renvoie l'élément au sommet. Renvoie None si la pile est vide. """ pass def est_vide(self): """Renvoie True si la pile est vide.""" pass def sommet(self): """ Renvoie l'élément au sommet sans le retirer. Renvoie None si la pile est vide. """ pass def taille(self): """Renvoie le nombre d'éléments dans la pile.""" pass def __repr__(self): """Affichage de la pile.""" pass ``` **Tests à effectuer :** ```python >>> p = Pile() >>> p.est_vide() True >>> p.empile("A") >>> p.empile("B") >>> p.empile("C") >>> p.sommet() 'C' >>> p.depile() 'C' >>> p.taille() 2 ``` --- ### Exercice 2 : La classe File Implémentez une classe `File` avec les méthodes suivantes : ```python class File: """Structure de données FIFO (First In First Out).""" def __init__(self): """Initialise une file vide.""" pass def enfile(self, element): """Ajoute un élément en queue de file.""" pass def defile(self): """ Retire et renvoie l'élément en tête de file. Renvoie None si la file est vide. """ pass def est_vide(self): """Renvoie True si la file est vide.""" pass def tete(self): """ Renvoie l'élément en tête sans le retirer. Renvoie None si la file est vide. """ pass def taille(self): """Renvoie le nombre d'éléments dans la file.""" pass def __repr__(self): """Affichage de la file.""" pass ``` **Tests à effectuer :** ```python >>> f = File() >>> f.est_vide() True >>> f.enfile("Premier") >>> f.enfile("Deuxième") >>> f.enfile("Troisième") >>> f.tete() 'Premier' >>> f.defile() 'Premier' >>> f.taille() 2 ``` --- ## Partie 2 : Simulateur de navigateur web ### Principe de fonctionnement Un navigateur utilise **deux piles** pour gérer l'historique : - **Pile `historique`** : contient les pages visitées (la page actuelle est au sommet) - **Pile `suivant`** : contient les pages "en avant" (après avoir cliqué sur Retour) | Action | Effet | |--------|-------| | Visiter une page | Empile dans `historique`, vide `suivant` | | Clic sur ← Retour | Dépile `historique` → empile dans `suivant` | | Clic sur → Suivant | Dépile `suivant` → empile dans `historique` | --- ### Exercice 3 : La classe Navigateur ```python class Navigateur: """Simulateur de navigateur web avec historique.""" def __init__(self, page_accueil="https://www.google.com"): """ Initialise le navigateur avec une page d'accueil. :param page_accueil: (str) URL de la page d'accueil """ self.historique = Pile() self.suivant = Pile() # À compléter : empiler la page d'accueil def page_actuelle(self): """ Renvoie l'URL de la page actuellement affichée. :return: (str) URL de la page actuelle ou None """ # À compléter pass def visiter(self, url): """ Visite une nouvelle page. - Empile l'URL dans l'historique - Vide la pile 'suivant' (on ne peut plus avancer) :param url: (str) URL de la page à visiter """ # À compléter pass def retour(self): """ Revient à la page précédente (bouton ←). - Dépile l'historique - Empile la page actuelle dans 'suivant' :return: (str) URL de la nouvelle page actuelle ou None si impossible """ # À compléter pass def avancer(self): """ Avance à la page suivante (bouton →). - Dépile 'suivant' - Empile dans l'historique :return: (str) URL de la nouvelle page actuelle ou None si impossible """ # À compléter pass def peut_reculer(self): """Renvoie True si le bouton Retour est actif.""" # À compléter pass def peut_avancer(self): """Renvoie True si le bouton Suivant est actif.""" # À compléter pass def afficher_etat(self): """Affiche l'état actuel du navigateur.""" retour = "←" if self.peut_reculer() else "✗" avancer = "→" if self.peut_avancer() else "✗" print(f"[{retour}] [{avancer}] | {self.page_actuelle()}") ``` **Tests à effectuer :** ```python >>> nav = Navigateur() >>> nav.afficher_etat() [✗] [✗] | https://www.google.com >>> nav.visiter("https://www.wikipedia.org") >>> nav.visiter("https://www.youtube.com") >>> nav.visiter("https://www.github.com") >>> nav.afficher_etat() [←] [✗] | https://www.github.com >>> nav.retour() 'https://www.youtube.com' >>> nav.afficher_etat() [←] [→] | https://www.youtube.com >>> nav.retour() 'https://www.wikipedia.org' >>> nav.avancer() 'https://www.youtube.com' >>> nav.visiter("https://www.python.org") >>> nav.afficher_etat() [←] [✗] | https://www.python.org >>> nav.peut_avancer() False # La pile 'suivant' a été vidée ``` --- ### Exercice 4 : Historique complet Ajoutez une méthode pour afficher tout l'historique de navigation : ```python def afficher_historique(self): """ Affiche l'historique complet de navigation. La page actuelle est marquée d'une flèche. """ # À compléter pass ``` **Exemple de sortie :** ``` Historique de navigation : 1. https://www.google.com 2. https://www.wikipedia.org 3. https://www.youtube.com → 4. https://www.github.com (page actuelle) ``` --- ## Partie 3 : File d'attente Netflix ### Contexte Sur Netflix, vous pouvez ajouter des films à votre liste "**À regarder**". Les films sont regardés dans l'ordre d'ajout (FIFO), mais vous pouvez aussi mettre un film en priorité. --- ### Exercice 5 : La classe ListeVisionnage ```python class ListeVisionnage: """Gestionnaire de file d'attente de visionnage.""" def __init__(self): """Initialise une liste de visionnage vide.""" self.file_attente = File() self.historique = Pile() # Films déjà regardés def ajouter(self, titre): """ Ajoute un film/série à la file d'attente. :param titre: (str) Titre du contenu à ajouter """ # À compléter pass def regarder_suivant(self): """ Regarde le prochain élément de la file. - Défile le contenu - L'ajoute à l'historique :return: (str) Titre du contenu regardé ou None """ # À compléter pass def prochain(self): """ Renvoie le prochain contenu sans le retirer. :return: (str) Titre du prochain contenu ou None """ # À compléter pass def revoir_dernier(self): """ Remet le dernier contenu regardé dans la file (en tête). Utile pour revoir un épisode. :return: (str) Titre du contenu remis en file ou None """ # À compléter (utiliser une file temporaire ou une autre approche) pass def afficher(self): """Affiche l'état de la liste de visionnage.""" print("=== Ma Liste Netflix ===") print(f"À regarder : {self.file_attente.taille()} élément(s)") print(f"Déjà vus : {self.historique.taille()} élément(s)") if not self.file_attente.est_vide(): print(f"Prochain : {self.prochain()}") ``` **Tests à effectuer :** ```python >>> netflix = ListeVisionnage() >>> netflix.ajouter("Stranger Things S5") >>> netflix.ajouter("Wednesday S2") >>> netflix.ajouter("Squid Game S3") >>> netflix.afficher() === Ma Liste Netflix === À regarder : 3 élément(s) Déjà vus : 0 élément(s) Prochain : Stranger Things S5 >>> netflix.regarder_suivant() 'Stranger Things S5' >>> netflix.regarder_suivant() 'Wednesday S2' >>> netflix.afficher() === Ma Liste Netflix === À regarder : 1 élément(s) Déjà vus : 2 élément(s) Prochain : Squid Game S3 >>> netflix.revoir_dernier() 'Wednesday S2' >>> netflix.prochain() 'Wednesday S2' ``` --- ### Exercice 6 : File avec priorité Netflix permet aussi de mettre un contenu "en haut de la liste". Ajoutez cette fonctionnalité : ```python def ajouter_prioritaire(self, titre): """ Ajoute un contenu en tête de file (sera regardé en premier). Nécessite de reconstruire la file. :param titre: (str) Titre du contenu prioritaire """ # Indice : créer une nouvelle file, enfiler le titre prioritaire, # puis transvaser l'ancienne file pass ``` **Test :** ```python >>> netflix.ajouter("Film 1") >>> netflix.ajouter("Film 2") >>> netflix.ajouter_prioritaire("Film URGENT") >>> netflix.prochain() 'Film URGENT' ``` --- ## Partie 4 : Système Undo/Redo ### Exercice 7 : Éditeur de texte simplifié Créez un éditeur qui permet d'annuler (Ctrl+Z) et de refaire (Ctrl+Y) des actions. ```python class EditeurTexte: """Éditeur de texte avec Undo/Redo.""" def __init__(self): """Initialise l'éditeur.""" self.texte = "" self.historique_undo = Pile() # États précédents self.historique_redo = Pile() # États annulés def ecrire(self, texte_ajoute): """ Ajoute du texte. Sauvegarde l'état actuel dans l'historique. :param texte_ajoute: (str) Texte à ajouter """ # À compléter pass def effacer(self, n=1): """ Efface les n derniers caractères. :param n: (int) Nombre de caractères à effacer """ # À compléter pass def undo(self): """ Annule la dernière action (Ctrl+Z). :return: (bool) True si l'annulation a réussi """ # À compléter pass def redo(self): """ Refait la dernière action annulée (Ctrl+Y). :return: (bool) True si le redo a réussi """ # À compléter pass def afficher(self): """Affiche le texte actuel.""" undo_dispo = "Ctrl+Z" if not self.historique_undo.est_vide() else "-----" redo_dispo = "Ctrl+Y" if not self.historique_redo.est_vide() else "-----" print(f"[{undo_dispo}] [{redo_dispo}]") print(f"Texte : \"{self.texte}\"") ``` **Tests :** ```python >>> editeur = EditeurTexte() >>> editeur.ecrire("Bonjour") >>> editeur.ecrire(" le monde") >>> editeur.afficher() [Ctrl+Z] [-----] Texte : "Bonjour le monde" >>> editeur.undo() True >>> editeur.afficher() [Ctrl+Z] [Ctrl+Y] Texte : "Bonjour" >>> editeur.redo() True >>> editeur.afficher() [Ctrl+Z] [-----] Texte : "Bonjour le monde" >>> editeur.effacer(6) >>> editeur.afficher() [Ctrl+Z] [-----] Texte : "Bonjour le" ``` --- ## Bonus : Comparaison des complexités | Opération | Pile (liste Python) | File (liste Python) | File (deux piles) | |-----------|---------------------|---------------------|-------------------| | Empile/Enfile | O(1) | O(n)* | O(1) amorti | | Dépile/Défile | O(1) | O(1) | O(1) amorti | | Sommet/Tête | O(1) | O(1) | O(1) | | Est_vide | O(1) | O(1) | O(1) | *L'insertion en début de liste Python est en O(n). **Exercice bonus** : Implémentez une file efficace en utilisant deux piles. --- ## Résumé des notions | Structure | Principe | Analogie | Cas d'usage | |-----------|----------|----------|-------------| | **Pile** | LIFO | Pile d'assiettes | Historique, Undo, Appels de fonctions | | **File** | FIFO | File d'attente | Impression, Streaming, BFS | --- Auteurs : Florian Mathieu Licence CC BY NC Licence Creative Commons
Ce cours est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.