ajout de tous les cours et TP préparés cet été
This commit is contained in:
494
Pile_File/TP/Corrige_TP_Navigateur.py
Normal file
494
Pile_File/TP/Corrige_TP_Navigateur.py
Normal file
@@ -0,0 +1,494 @@
|
||||
"""
|
||||
Corrigé du TP Navigateur — Piles et Files en action
|
||||
"""
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# PARTIE 1 : Implémentation des structures
|
||||
# =============================================================================
|
||||
|
||||
class Pile:
|
||||
"""Structure de données LIFO (Last In First Out)."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialise une pile vide."""
|
||||
self.elements = []
|
||||
|
||||
def empile(self, element):
|
||||
"""Ajoute un élément au sommet de la pile."""
|
||||
self.elements.append(element)
|
||||
|
||||
def depile(self):
|
||||
"""
|
||||
Retire et renvoie l'élément au sommet.
|
||||
Renvoie None si la pile est vide.
|
||||
"""
|
||||
if self.est_vide():
|
||||
return None
|
||||
return self.elements.pop()
|
||||
|
||||
def est_vide(self):
|
||||
"""Renvoie True si la pile est vide."""
|
||||
return len(self.elements) == 0
|
||||
|
||||
def sommet(self):
|
||||
"""
|
||||
Renvoie l'élément au sommet sans le retirer.
|
||||
Renvoie None si la pile est vide.
|
||||
"""
|
||||
if self.est_vide():
|
||||
return None
|
||||
return self.elements[-1]
|
||||
|
||||
def taille(self):
|
||||
"""Renvoie le nombre d'éléments dans la pile."""
|
||||
return len(self.elements)
|
||||
|
||||
def __repr__(self):
|
||||
"""Affichage de la pile."""
|
||||
if self.est_vide():
|
||||
return "Pile vide"
|
||||
result = "Sommet\n"
|
||||
for i in range(len(self.elements) - 1, -1, -1):
|
||||
result += f" | {self.elements[i]} |\n"
|
||||
result += "Fond"
|
||||
return result
|
||||
|
||||
|
||||
class File:
|
||||
"""Structure de données FIFO (First In First Out)."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialise une file vide."""
|
||||
self.elements = []
|
||||
|
||||
def enfile(self, element):
|
||||
"""Ajoute un élément en queue de file."""
|
||||
self.elements.append(element)
|
||||
|
||||
def defile(self):
|
||||
"""
|
||||
Retire et renvoie l'élément en tête de file.
|
||||
Renvoie None si la file est vide.
|
||||
"""
|
||||
if self.est_vide():
|
||||
return None
|
||||
return self.elements.pop(0)
|
||||
|
||||
def est_vide(self):
|
||||
"""Renvoie True si la file est vide."""
|
||||
return len(self.elements) == 0
|
||||
|
||||
def tete(self):
|
||||
"""
|
||||
Renvoie l'élément en tête sans le retirer.
|
||||
Renvoie None si la file est vide.
|
||||
"""
|
||||
if self.est_vide():
|
||||
return None
|
||||
return self.elements[0]
|
||||
|
||||
def taille(self):
|
||||
"""Renvoie le nombre d'éléments dans la file."""
|
||||
return len(self.elements)
|
||||
|
||||
def __repr__(self):
|
||||
"""Affichage de la file."""
|
||||
if self.est_vide():
|
||||
return "File vide"
|
||||
result = "Tête → "
|
||||
result += " → ".join(str(e) for e in self.elements)
|
||||
result += " → Queue"
|
||||
return result
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# PARTIE 2 : Simulateur de navigateur web
|
||||
# =============================================================================
|
||||
|
||||
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()
|
||||
self.historique.empile(page_accueil)
|
||||
|
||||
def page_actuelle(self):
|
||||
"""
|
||||
Renvoie l'URL de la page actuellement affichée.
|
||||
|
||||
:return: (str) URL de la page actuelle ou None
|
||||
"""
|
||||
return self.historique.sommet()
|
||||
|
||||
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
|
||||
"""
|
||||
self.historique.empile(url)
|
||||
# Vider la pile suivant
|
||||
while not self.suivant.est_vide():
|
||||
self.suivant.depile()
|
||||
|
||||
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
|
||||
"""
|
||||
if not self.peut_reculer():
|
||||
return None
|
||||
|
||||
page_actuelle = self.historique.depile()
|
||||
self.suivant.empile(page_actuelle)
|
||||
return self.page_actuelle()
|
||||
|
||||
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
|
||||
"""
|
||||
if not self.peut_avancer():
|
||||
return None
|
||||
|
||||
page_suivante = self.suivant.depile()
|
||||
self.historique.empile(page_suivante)
|
||||
return self.page_actuelle()
|
||||
|
||||
def peut_reculer(self):
|
||||
"""Renvoie True si le bouton Retour est actif."""
|
||||
return self.historique.taille() > 1
|
||||
|
||||
def peut_avancer(self):
|
||||
"""Renvoie True si le bouton Suivant est actif."""
|
||||
return not self.suivant.est_vide()
|
||||
|
||||
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()}")
|
||||
|
||||
def afficher_historique(self):
|
||||
"""
|
||||
Affiche l'historique complet de navigation.
|
||||
La page actuelle est marquée d'une flèche.
|
||||
"""
|
||||
print("Historique de navigation :")
|
||||
# Copier la pile pour l'afficher sans la modifier
|
||||
temp = Pile()
|
||||
pages = []
|
||||
|
||||
while not self.historique.est_vide():
|
||||
page = self.historique.depile()
|
||||
pages.append(page)
|
||||
temp.empile(page)
|
||||
|
||||
# Restaurer la pile
|
||||
while not temp.est_vide():
|
||||
self.historique.empile(temp.depile())
|
||||
|
||||
# Afficher (inverser pour avoir l'ordre chronologique)
|
||||
pages.reverse()
|
||||
for i, page in enumerate(pages, 1):
|
||||
if i == len(pages):
|
||||
print(f"→ {i}. {page} (page actuelle)")
|
||||
else:
|
||||
print(f" {i}. {page}")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# PARTIE 3 : File d'attente Netflix
|
||||
# =============================================================================
|
||||
|
||||
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()
|
||||
|
||||
def ajouter(self, titre):
|
||||
"""
|
||||
Ajoute un film/série à la file d'attente.
|
||||
|
||||
:param titre: (str) Titre du contenu à ajouter
|
||||
"""
|
||||
self.file_attente.enfile(titre)
|
||||
|
||||
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
|
||||
"""
|
||||
if self.file_attente.est_vide():
|
||||
return None
|
||||
|
||||
contenu = self.file_attente.defile()
|
||||
self.historique.empile(contenu)
|
||||
return contenu
|
||||
|
||||
def prochain(self):
|
||||
"""
|
||||
Renvoie le prochain contenu sans le retirer.
|
||||
|
||||
:return: (str) Titre du prochain contenu ou None
|
||||
"""
|
||||
return self.file_attente.tete()
|
||||
|
||||
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
|
||||
"""
|
||||
if self.historique.est_vide():
|
||||
return None
|
||||
|
||||
contenu = self.historique.depile()
|
||||
# Créer une nouvelle file avec ce contenu en tête
|
||||
nouvelle_file = File()
|
||||
nouvelle_file.enfile(contenu)
|
||||
|
||||
# Transvaser l'ancienne file
|
||||
while not self.file_attente.est_vide():
|
||||
nouvelle_file.enfile(self.file_attente.defile())
|
||||
|
||||
self.file_attente = nouvelle_file
|
||||
return contenu
|
||||
|
||||
def ajouter_prioritaire(self, titre):
|
||||
"""
|
||||
Ajoute un contenu en tête de file (sera regardé en premier).
|
||||
|
||||
:param titre: (str) Titre du contenu prioritaire
|
||||
"""
|
||||
nouvelle_file = File()
|
||||
nouvelle_file.enfile(titre)
|
||||
|
||||
while not self.file_attente.est_vide():
|
||||
nouvelle_file.enfile(self.file_attente.defile())
|
||||
|
||||
self.file_attente = nouvelle_file
|
||||
|
||||
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()}")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# PARTIE 4 : Système Undo/Redo
|
||||
# =============================================================================
|
||||
|
||||
class EditeurTexte:
|
||||
"""Éditeur de texte avec Undo/Redo."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialise l'éditeur."""
|
||||
self.texte = ""
|
||||
self.historique_undo = Pile()
|
||||
self.historique_redo = Pile()
|
||||
|
||||
def ecrire(self, texte_ajoute):
|
||||
"""
|
||||
Ajoute du texte.
|
||||
Sauvegarde l'état actuel dans l'historique.
|
||||
|
||||
:param texte_ajoute: (str) Texte à ajouter
|
||||
"""
|
||||
self.historique_undo.empile(self.texte)
|
||||
self.texte += texte_ajoute
|
||||
# Vider l'historique redo car nouvelle action
|
||||
while not self.historique_redo.est_vide():
|
||||
self.historique_redo.depile()
|
||||
|
||||
def effacer(self, n=1):
|
||||
"""
|
||||
Efface les n derniers caractères.
|
||||
|
||||
:param n: (int) Nombre de caractères à effacer
|
||||
"""
|
||||
if n > len(self.texte):
|
||||
n = len(self.texte)
|
||||
|
||||
self.historique_undo.empile(self.texte)
|
||||
self.texte = self.texte[:-n] if n > 0 else self.texte
|
||||
# Vider l'historique redo
|
||||
while not self.historique_redo.est_vide():
|
||||
self.historique_redo.depile()
|
||||
|
||||
def undo(self):
|
||||
"""
|
||||
Annule la dernière action (Ctrl+Z).
|
||||
|
||||
:return: (bool) True si l'annulation a réussi
|
||||
"""
|
||||
if self.historique_undo.est_vide():
|
||||
return False
|
||||
|
||||
self.historique_redo.empile(self.texte)
|
||||
self.texte = self.historique_undo.depile()
|
||||
return True
|
||||
|
||||
def redo(self):
|
||||
"""
|
||||
Refait la dernière action annulée (Ctrl+Y).
|
||||
|
||||
:return: (bool) True si le redo a réussi
|
||||
"""
|
||||
if self.historique_redo.est_vide():
|
||||
return False
|
||||
|
||||
self.historique_undo.empile(self.texte)
|
||||
self.texte = self.historique_redo.depile()
|
||||
return True
|
||||
|
||||
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}"')
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# BONUS : File avec deux piles (complexité amortie O(1))
|
||||
# =============================================================================
|
||||
|
||||
class FileDeuxPiles:
|
||||
"""
|
||||
Implémentation efficace d'une file avec deux piles.
|
||||
Complexité amortie O(1) pour toutes les opérations.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialise une file vide."""
|
||||
self.pile_entree = Pile() # Pour les enfilements
|
||||
self.pile_sortie = Pile() # Pour les défilements
|
||||
|
||||
def enfile(self, element):
|
||||
"""Ajoute un élément en queue de file. O(1)"""
|
||||
self.pile_entree.empile(element)
|
||||
|
||||
def _transferer(self):
|
||||
"""Transfère pile_entree vers pile_sortie si nécessaire."""
|
||||
if self.pile_sortie.est_vide():
|
||||
while not self.pile_entree.est_vide():
|
||||
self.pile_sortie.empile(self.pile_entree.depile())
|
||||
|
||||
def defile(self):
|
||||
"""Retire et renvoie l'élément en tête. O(1) amorti."""
|
||||
self._transferer()
|
||||
return self.pile_sortie.depile()
|
||||
|
||||
def tete(self):
|
||||
"""Renvoie l'élément en tête sans le retirer. O(1) amorti."""
|
||||
self._transferer()
|
||||
return self.pile_sortie.sommet()
|
||||
|
||||
def est_vide(self):
|
||||
"""Renvoie True si la file est vide. O(1)"""
|
||||
return self.pile_entree.est_vide() and self.pile_sortie.est_vide()
|
||||
|
||||
def taille(self):
|
||||
"""Renvoie le nombre d'éléments. O(1)"""
|
||||
return self.pile_entree.taille() + self.pile_sortie.taille()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# TESTS
|
||||
# =============================================================================
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("TEST DU NAVIGATEUR")
|
||||
print("=" * 60)
|
||||
|
||||
nav = Navigateur()
|
||||
nav.afficher_etat()
|
||||
|
||||
nav.visiter("https://www.wikipedia.org")
|
||||
nav.visiter("https://www.youtube.com")
|
||||
nav.visiter("https://www.github.com")
|
||||
nav.afficher_etat()
|
||||
|
||||
print("\nRetour...")
|
||||
nav.retour()
|
||||
nav.afficher_etat()
|
||||
|
||||
print("\nRetour...")
|
||||
nav.retour()
|
||||
nav.afficher_etat()
|
||||
|
||||
print("\nAvancer...")
|
||||
nav.avancer()
|
||||
nav.afficher_etat()
|
||||
|
||||
print("\nVisite nouvelle page...")
|
||||
nav.visiter("https://www.python.org")
|
||||
nav.afficher_etat()
|
||||
|
||||
print("\n")
|
||||
nav.afficher_historique()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("TEST DE NETFLIX")
|
||||
print("=" * 60)
|
||||
|
||||
netflix = ListeVisionnage()
|
||||
netflix.ajouter("Stranger Things S5")
|
||||
netflix.ajouter("Wednesday S2")
|
||||
netflix.ajouter("Squid Game S3")
|
||||
netflix.afficher()
|
||||
|
||||
print(f"\nRegardé : {netflix.regarder_suivant()}")
|
||||
print(f"Regardé : {netflix.regarder_suivant()}")
|
||||
netflix.afficher()
|
||||
|
||||
print(f"\nRevoir dernier : {netflix.revoir_dernier()}")
|
||||
netflix.afficher()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("TEST DE L'EDITEUR")
|
||||
print("=" * 60)
|
||||
|
||||
editeur = EditeurTexte()
|
||||
editeur.ecrire("Bonjour")
|
||||
editeur.ecrire(" le monde")
|
||||
editeur.afficher()
|
||||
|
||||
print("\nUndo...")
|
||||
editeur.undo()
|
||||
editeur.afficher()
|
||||
|
||||
print("\nRedo...")
|
||||
editeur.redo()
|
||||
editeur.afficher()
|
||||
|
||||
print("\nEffacer 6 caractères...")
|
||||
editeur.effacer(6)
|
||||
editeur.afficher()
|
||||
@@ -53,8 +53,8 @@ class File1 :
|
||||
Fonction qui ajoute un élément, si la taille le permet.
|
||||
return (bool): Renvoie True si l'ajout de l'élément a eu lieu, False sinon
|
||||
"""
|
||||
if file.taille() < 5 :
|
||||
file.enfile(fichier)
|
||||
if self.taille() < 7 :
|
||||
self.enfile(element)
|
||||
return True
|
||||
else :
|
||||
return False
|
||||
@@ -63,8 +63,8 @@ class File1 :
|
||||
"""
|
||||
Fonction qui vide une file et affiche ces éléments
|
||||
"""
|
||||
while file.est_vide() == False :
|
||||
e = file.defile()
|
||||
while self.est_vide() == False :
|
||||
e = self.defile()
|
||||
print(e)
|
||||
|
||||
# PARTIE 3 :
|
||||
|
||||
@@ -27,7 +27,7 @@ Supposons que notre file à une taille fixe, disons 7 éléments maximum.
|
||||
|
||||
## 3. Deuxième implémentation
|
||||
|
||||
Ici nous allons implémenter la file de manière récursive. Elle possèdera deux attributs, le premier sera un élément de la pile nommé tête, le second sera une autre File. Cette seconde file possèdera elle aura un élément et une autre file en attribut. Ainsi de suite.
|
||||
Ici nous allons implémenter la file de manière récursive. Elle possèdera deux attributs, le premier sera un élément de la file nommé tête, le second sera une autre File. Cette seconde file possèdera elle aura un élément et une autre file en attribut. Ainsi de suite.
|
||||
|
||||
1. Programmez cette classe nommée File2 avec les méthodes suivantes
|
||||
* **enfile( )**
|
||||
@@ -44,11 +44,11 @@ Chacun tire la carte du dessus de son paquet et la pose sur la table. Celui qui
|
||||
|
||||
Lorsqu'il y a "bataille" les joueurs tirent la carte suivante et la posent, face cachée, sur la carte précédente. Puis ils tirent une deuxième carte qu'ils posent cette fois-ci face découverte et c'est cette dernière qui départagera les joueurs.
|
||||
|
||||
Lorsqu'il y a bataille et qu'un des deux joueur à moins de 3 cartes alors il a perdu
|
||||
Lorsqu'il y a bataille et qu'un des deux joueurs a moins de 3 cartes alors il a perdu.
|
||||
|
||||
Le gagnant est celui qui remporte toutes les cartes.
|
||||
|
||||
Le jeu de la bataille peut être facilement coder avec des Files.
|
||||
Le jeu de la bataille peut être facilement codé avec des Files.
|
||||
|
||||
Un fichier carte.py contient la classe carte et les fonctions suivantes :
|
||||
|
||||
@@ -69,4 +69,12 @@ Un fichier carte.py contient la classe carte et les fonctions suivantes :
|
||||
|
||||
## Pour aller plus loin :
|
||||
|
||||
Ecrire une nouvelle implémentation d'une file, cette fois-ci il faut manipuler la file avec deux piles.
|
||||
Écrire une nouvelle implémentation d'une file, cette fois-ci il faut manipuler la file avec deux piles.
|
||||
|
||||
---
|
||||
|
||||
Auteurs : Florian Mathieu, Enzo Frémeaux, Thimothée Decooster
|
||||
|
||||
Licence CC BY NC
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.
|
||||
530
Pile_File/TP/TP_Navigateur.md
Normal file
530
Pile_File/TP/TP_Navigateur.md
Normal file
@@ -0,0 +1,530 @@
|
||||
# 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
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.
|
||||
@@ -44,7 +44,7 @@ Afin d'implémenter la pile il nous faut utiliser une structure de données perm
|
||||
|
||||
|
||||
|
||||
## 2. 2. Calculatrice polonaise inverse
|
||||
### 2. 2. Calculatrice polonaise inverse
|
||||
|
||||
La calculatrice polonaise inverse permet de faire des calculs simple mais pose l'opérateur après les deux opérandes.
|
||||
|
||||
@@ -75,7 +75,7 @@ La calculatrice polonaise inverse permet de faire des calculs simple mais pose l
|
||||
|
||||
Le but ici est de trier une pile. Pour cela nous utiliserons une autre pile temporaire permettant de stocker les éléments.
|
||||
|
||||
1. Ecrire une fonction tri_pile( ) prennant en paramètre une pile et renvoyant la pile triée.
|
||||
1. Écrire une fonction tri_pile( ) prenant en paramètre une pile et renvoyant la pile triée.
|
||||
|
||||
> L'idée ici est d'utiliser seulement deux piles. Afin de comprendre le fonctionnement il faut faire quelques essais à la main.
|
||||
|
||||
@@ -98,3 +98,10 @@ Le but ici est de trier une pile. Pour cela nous utiliserons une autre pile temp
|
||||
9
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Auteurs : Florian Mathieu, Enzo Frémeaux, Thimothée Decooster
|
||||
|
||||
Licence CC BY NC
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.
|
||||
|
||||
@@ -8,8 +8,8 @@ class Pile :
|
||||
|
||||
def empile(self,x) :
|
||||
"""
|
||||
Méthode qui enpile un élément
|
||||
param x : () Elément x à enpiler
|
||||
Méthode qui empile un élément
|
||||
param x : () Elément x à empiler
|
||||
"""
|
||||
self.pile.append(x)
|
||||
|
||||
@@ -52,8 +52,8 @@ class Pile :
|
||||
while pile_tmp.taille() != 0 and pile_tmp.top() < val_tmp:
|
||||
self.empile(pile_tmp.depile())
|
||||
pile_tmp.empile(val_tmp)
|
||||
p.pile = pile_tmp.pile
|
||||
return p
|
||||
self.pile = pile_tmp.pile
|
||||
return self
|
||||
|
||||
|
||||
p = Pile()
|
||||
|
||||
Reference in New Issue
Block a user