1688 lines
44 KiB
Markdown
1688 lines
44 KiB
Markdown
# Chapitre 03 — Les boucles : répéter sans se répéter
|
||
|
||
> **⏱️ Durée estimée :** 5 heures
|
||
>
|
||
> **📋 Prérequis :** [Chapitre 02 — Les conditions](02-conditions.md) : `if`/`elif`/`else`, opérateurs de comparaison et logiques, indentation des blocs.
|
||
>
|
||
> **🎯 Objectifs — À la fin de ce chapitre, tu sauras :**
|
||
> - répéter des instructions un nombre connu de fois avec `for` et `range()` ;
|
||
> - parcourir les caractères d'une chaîne et les éléments d'une liste simple ;
|
||
> - répéter tant qu'une condition est vraie avec `while`, et éviter les boucles infinies ;
|
||
> - contrôler une boucle avec `break` et `continue`, et utiliser le `else` de boucle ;
|
||
> - utiliser les motifs classiques : compteur, accumulateur, recherche du maximum, saisie validée ;
|
||
> - imbriquer des boucles pour produire des tableaux et des motifs.
|
||
|
||
---
|
||
|
||
## Cours
|
||
|
||
### 1. Pourquoi des boucles ?
|
||
|
||
Imagine qu'on te demande d'afficher les nombres de 1 à 5. Facile :
|
||
|
||
```python
|
||
print(1)
|
||
print(2)
|
||
print(3)
|
||
print(4)
|
||
print(5)
|
||
```
|
||
|
||
Maintenant, de 1 à 1000. Tu ne vas pas écrire 1000 lignes ! Et si le nombre est choisi par l'utilisateur, c'est même impossible à écrire à l'avance.
|
||
|
||
Une **boucle** permet de répéter un bloc d'instructions plusieurs fois. C'est l'une des forces des ordinateurs : ils ne s'ennuient jamais et ne se trompent pas en répétant.
|
||
|
||
```python
|
||
for nombre in range(1, 6):
|
||
print(nombre)
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
```
|
||
|
||
Deux lignes, et pour aller jusqu'à 1000, il suffit de changer `6` en `1001`.
|
||
|
||
Python propose deux sortes de boucles :
|
||
|
||
- **`for`** : « pour chaque élément de… ». On l'utilise quand on sait **sur quoi** on boucle (une plage de nombres, les lettres d'un mot, les éléments d'une liste).
|
||
- **`while`** : « tant que… ». On l'utilise quand on ne sait pas à l'avance combien de tours il faudra (attendre une bonne réponse, jouer jusqu'à la victoire).
|
||
|
||
### 2. La boucle `for` avec `range()`
|
||
|
||
#### 2.1. Structure
|
||
|
||
```python
|
||
for i in range(3):
|
||
print("Bonjour !")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Bonjour !
|
||
Bonjour !
|
||
Bonjour !
|
||
```
|
||
|
||
La structure ressemble à celle de `if` : une ligne qui se termine par `:`, puis un **bloc indenté** qui sera répété. Chaque répétition s'appelle un **tour** (ou une **itération**).
|
||
|
||
À chaque tour, la **variable de boucle** (ici `i`) prend une nouvelle valeur. On peut s'en servir dans le bloc :
|
||
|
||
```python
|
||
for i in range(3):
|
||
print(f"Tour numéro {i}")
|
||
print("Fini !")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Tour numéro 0
|
||
Tour numéro 1
|
||
Tour numéro 2
|
||
Fini !
|
||
```
|
||
|
||
Remarques importantes :
|
||
|
||
- `range(3)` produit les nombres **0, 1, 2** : il commence à **0** et s'arrête **avant** 3. Il y a bien 3 tours.
|
||
- Le `print("Fini !")`, non indenté, est exécuté **une seule fois**, après la fin de la boucle.
|
||
|
||
#### 2.2. Les trois formes de `range()`
|
||
|
||
| Écriture | Nombres produits | Explication |
|
||
|---|---|---|
|
||
| `range(fin)` | 0, 1, …, fin − 1 | de 0 jusqu'à `fin` exclu |
|
||
| `range(debut, fin)` | debut, …, fin − 1 | de `debut` inclus à `fin` exclu |
|
||
| `range(debut, fin, pas)` | debut, debut + pas, … | en avançant de `pas` à chaque fois |
|
||
|
||
```python
|
||
print("range(5) :", end=" ")
|
||
for i in range(5):
|
||
print(i, end=" ")
|
||
print()
|
||
|
||
print("range(2, 6) :", end=" ")
|
||
for i in range(2, 6):
|
||
print(i, end=" ")
|
||
print()
|
||
|
||
print("range(0, 20, 5) :", end=" ")
|
||
for i in range(0, 20, 5):
|
||
print(i, end=" ")
|
||
print()
|
||
|
||
print("range(5, 0, -1) :", end=" ")
|
||
for i in range(5, 0, -1):
|
||
print(i, end=" ")
|
||
print()
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
range(5) : 0 1 2 3 4
|
||
range(2, 6) : 2 3 4 5
|
||
range(0, 20, 5) : 0 5 10 15
|
||
range(5, 0, -1) : 5 4 3 2 1
|
||
```
|
||
|
||
On utilise ici `end=" "` (vu au chapitre 01) pour afficher les nombres sur une même ligne, puis un `print()` vide pour passer à la ligne.
|
||
|
||
> 💡 **Pourquoi la fin est-elle exclue ?** C'est une convention qui simplifie beaucoup de choses : `range(n)` fait exactement `n` tours, et `range(a, b)` fait `b - a` tours. Pour aller **jusqu'à** `n` inclus, écris `range(1, n + 1)`.
|
||
|
||
Avec un pas négatif, on compte à rebours. Un compte à rebours de fusée :
|
||
|
||
```python
|
||
for seconde in range(5, 0, -1):
|
||
print(f"{seconde}...")
|
||
print("Décollage ! 🚀")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
5...
|
||
4...
|
||
3...
|
||
2...
|
||
1...
|
||
Décollage ! 🚀
|
||
```
|
||
|
||
`range()` n'accepte que des **entiers** : `range(3.5)` provoque une erreur (voir les pièges).
|
||
|
||
#### 2.3. La variable de boucle
|
||
|
||
Le nom de la variable de boucle est libre. On utilise souvent `i` (pour *indice*) quand c'est un simple compteur, mais un nom parlant est préférable :
|
||
|
||
```python
|
||
for annee in range(2020, 2026):
|
||
print(f"Bonne année {annee} !")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Bonne année 2020 !
|
||
Bonne année 2021 !
|
||
Bonne année 2022 !
|
||
Bonne année 2023 !
|
||
Bonne année 2024 !
|
||
Bonne année 2025 !
|
||
```
|
||
|
||
Si tu n'as pas besoin de la variable, la convention est de l'appeler `_` :
|
||
|
||
```python
|
||
for _ in range(3):
|
||
print("Hip hip hip, hourra !")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Hip hip hip, hourra !
|
||
Hip hip hip, hourra !
|
||
Hip hip hip, hourra !
|
||
```
|
||
|
||
#### 2.4. Le nombre de tours peut venir de l'utilisateur
|
||
|
||
```python
|
||
n = int(input("Jusqu'à combien veux-tu compter ? "))
|
||
for nombre in range(1, n + 1):
|
||
print(nombre, end=" ")
|
||
print()
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Jusqu'à combien veux-tu compter ? 7
|
||
1 2 3 4 5 6 7
|
||
```
|
||
|
||
### 3. Parcourir une chaîne et une liste simple
|
||
|
||
`for` ne fonctionne pas qu'avec `range()`. Il sait parcourir **tout ce qui contient plusieurs éléments**. On dit que ces objets sont **itérables**.
|
||
|
||
#### 3.1. Les caractères d'une chaîne
|
||
|
||
```python
|
||
mot = "Python"
|
||
for lettre in mot:
|
||
print(lettre)
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
P
|
||
y
|
||
t
|
||
h
|
||
o
|
||
n
|
||
```
|
||
|
||
À chaque tour, `lettre` contient le caractère suivant. Pratique pour analyser un texte :
|
||
|
||
```python
|
||
phrase = "Le chat dort sur le canapé"
|
||
nb_espaces = 0
|
||
for caractere in phrase:
|
||
if caractere == " ":
|
||
nb_espaces += 1
|
||
print(f"Il y a {nb_espaces} espaces, donc {nb_espaces + 1} mots.")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Il y a 5 espaces, donc 6 mots.
|
||
```
|
||
|
||
Remarque comment on combine une boucle et une condition : le `if` est **dans** la boucle, il a donc 4 espaces d'indentation de plus.
|
||
|
||
#### 3.2. Un aperçu des listes
|
||
|
||
Une **liste** est une suite de valeurs, écrite entre crochets `[ ]` et séparée par des virgules. Les listes ont de très nombreuses possibilités, qui seront détaillées au [chapitre 04](04-structures-de-donnees.md). Pour l'instant, retiens simplement qu'on peut les parcourir avec `for` :
|
||
|
||
```python
|
||
fruits = ["pomme", "banane", "cerise"]
|
||
for fruit in fruits:
|
||
print(f"J'aime la {fruit}.")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
J'aime la pomme.
|
||
J'aime la banane.
|
||
J'aime la cerise.
|
||
```
|
||
|
||
```python
|
||
notes = [12, 15.5, 9, 17]
|
||
for note in notes:
|
||
if note >= 10:
|
||
print(f"{note} : admis")
|
||
else:
|
||
print(f"{note} : non admis")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
12 : admis
|
||
15.5 : admis
|
||
9 : non admis
|
||
17 : admis
|
||
```
|
||
|
||
> 💡 **Convention de nommage :** une liste a un nom au pluriel (`fruits`, `notes`), la variable de boucle le même nom au singulier (`fruit`, `note`). Le code se lit alors presque comme du français : « pour chaque fruit dans fruits ».
|
||
|
||
### 4. Les motifs classiques avec `for`
|
||
|
||
Certaines façons d'utiliser les boucles reviennent sans cesse. Les connaître, c'est avoir une boîte à outils toute prête.
|
||
|
||
#### 4.1. L'accumulateur : faire une somme
|
||
|
||
Pour additionner une série de nombres, on part d'une variable à **0**, et on lui ajoute chaque nombre à chaque tour :
|
||
|
||
```python
|
||
total = 0
|
||
for nombre in range(1, 101):
|
||
total += nombre
|
||
print(f"La somme des nombres de 1 à 100 vaut {total}.")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
La somme des nombres de 1 à 100 vaut 5050.
|
||
```
|
||
|
||
Pense à une tirelire : elle est vide au départ (`total = 0`), et à chaque tour on y glisse une pièce (`total += nombre`).
|
||
|
||
**Suivre l'exécution à la main** est un excellent exercice. Avec `range(1, 5)` :
|
||
|
||
| Tour | `nombre` | `total` avant | `total` après |
|
||
|---|---|---|---|
|
||
| 1 | 1 | 0 | 1 |
|
||
| 2 | 2 | 1 | 3 |
|
||
| 3 | 3 | 3 | 6 |
|
||
| 4 | 4 | 6 | 10 |
|
||
|
||
> ⚠️ L'initialisation `total = 0` doit être **avant** la boucle. Si tu la mets dedans, le total est remis à zéro à chaque tour !
|
||
|
||
Le même principe fonctionne pour un **produit** (en partant de **1**, pas de 0) :
|
||
|
||
```python
|
||
n = 5
|
||
factorielle = 1
|
||
for i in range(1, n + 1):
|
||
factorielle *= i
|
||
print(f"{n}! = {factorielle}")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
5! = 120
|
||
```
|
||
|
||
Et même pour construire une **chaîne**, en partant d'une chaîne vide :
|
||
|
||
```python
|
||
mot = "bonjour"
|
||
a_l_envers = ""
|
||
for lettre in mot:
|
||
a_l_envers = lettre + a_l_envers # on ajoute chaque lettre AU DÉBUT
|
||
print(a_l_envers)
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
ruojnob
|
||
```
|
||
|
||
#### 4.2. Le compteur : compter ce qui vérifie une condition
|
||
|
||
On part de 0, et on ajoute 1 seulement quand la condition est vraie :
|
||
|
||
```python
|
||
texte = "Anticonstitutionnellement"
|
||
nb_voyelles = 0
|
||
for lettre in texte.lower():
|
||
if lettre in "aeiouy":
|
||
nb_voyelles += 1
|
||
print(f"{texte} contient {nb_voyelles} voyelles.")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Anticonstitutionnellement contient 10 voyelles.
|
||
```
|
||
|
||
`texte.lower()` (vu au chapitre 02) met tout en minuscules, pour que le `A` majuscule soit aussi compté.
|
||
|
||
#### 4.3. La saisie répétée : calculer une moyenne
|
||
|
||
```python
|
||
nb_notes = int(input("Combien de notes ? "))
|
||
somme = 0
|
||
for i in range(1, nb_notes + 1):
|
||
note = float(input(f"Note {i} : "))
|
||
somme += note
|
||
moyenne = somme / nb_notes
|
||
print(f"Moyenne : {moyenne:.2f}")
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Combien de notes ? 3
|
||
Note 1 : 12
|
||
Note 2 : 15.5
|
||
Note 3 : 9
|
||
Moyenne : 12.17
|
||
```
|
||
|
||
#### 4.4. Chercher le maximum (ou le minimum)
|
||
|
||
On garde en mémoire « le plus grand vu jusqu'ici », et on le met à jour quand on trouve mieux :
|
||
|
||
```python
|
||
temperatures = [14, 19, 23, 17, 21]
|
||
plus_chaude = temperatures[0] # on suppose que la première est la plus grande
|
||
for temperature in temperatures:
|
||
if temperature > plus_chaude:
|
||
plus_chaude = temperature
|
||
print(f"Température maximale : {plus_chaude} °C")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Température maximale : 23 °C
|
||
```
|
||
|
||
`temperatures[0]` désigne le premier élément de la liste (les positions commencent à 0, comme `range`). Bien sûr, `max(temperatures)` ferait le même travail, mais comprendre ce motif est essentiel : tu en auras besoin pour des recherches plus complexes (« l'élève qui a la meilleure note », « le produit le moins cher en stock »…).
|
||
|
||
### 5. La boucle `while`
|
||
|
||
#### 5.1. Structure
|
||
|
||
`while` signifie « tant que ». La boucle répète son bloc **tant que** la condition est vraie :
|
||
|
||
```python
|
||
compteur = 1
|
||
while compteur <= 5:
|
||
print(f"Compteur : {compteur}")
|
||
compteur += 1
|
||
print("Terminé")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Compteur : 1
|
||
Compteur : 2
|
||
Compteur : 3
|
||
Compteur : 4
|
||
Compteur : 5
|
||
Terminé
|
||
```
|
||
|
||
Le déroulement :
|
||
|
||
1. Python évalue la condition `compteur <= 5`.
|
||
2. Si elle est **vraie**, il exécute le bloc, puis **revient à l'étape 1**.
|
||
3. Si elle est **fausse**, il saute le bloc et continue après la boucle.
|
||
|
||
C'est comme un `if` qui se répète. Si la condition est fausse dès le départ, le bloc n'est **jamais** exécuté.
|
||
|
||
> ⚠️ **Les trois ingrédients d'un `while` correct :**
|
||
> 1. une variable **initialisée avant** la boucle (`compteur = 1`) ;
|
||
> 2. une **condition** qui la teste (`compteur <= 5`) ;
|
||
> 3. une **modification** de cette variable dans le bloc (`compteur += 1`), pour que la condition finisse par devenir fausse.
|
||
|
||
#### 5.2. La boucle infinie
|
||
|
||
Si tu oublies le troisième ingrédient, la condition reste vraie pour toujours : c'est une **boucle infinie**.
|
||
|
||
```python
|
||
compteur = 0
|
||
while compteur < 5:
|
||
print("Tour", compteur)
|
||
# oubli de compteur += 1 !
|
||
```
|
||
|
||
Le programme affiche `Tour 0` à l'infini. **Pour l'arrêter, appuie sur `Ctrl` + `C`** dans le terminal. Python s'interrompt alors avec ce message, qui n'a rien d'inquiétant :
|
||
|
||
```text
|
||
Traceback (most recent call last):
|
||
File "boucle.py", line 3, in <module>
|
||
print("Tour", compteur)
|
||
~~~~~^^^^^^^^^^^^^^^^^^
|
||
KeyboardInterrupt
|
||
```
|
||
|
||
Tout programmeur écrit des boucles infinies par accident de temps en temps. Pas de panique : `Ctrl` + `C`.
|
||
|
||
#### 5.3. Quand utiliser `while` ?
|
||
|
||
Toute boucle `for i in range(...)` peut s'écrire avec `while`, mais `for` est alors plus simple et plus sûr (pas de risque d'oublier l'incrémentation). Réserve `while` aux situations où **le nombre de tours n'est pas connu à l'avance**.
|
||
|
||
Exemple : combien d'années faut-il pour doubler une épargne placée à 3 % par an ?
|
||
|
||
```python
|
||
capital = 1000
|
||
objectif = 2000
|
||
annees = 0
|
||
while capital < objectif:
|
||
capital *= 1.03
|
||
annees += 1
|
||
print(f"Il faut {annees} ans pour atteindre {capital:.2f} €.")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Il faut 24 ans pour atteindre 2032.79 €.
|
||
```
|
||
|
||
On ne pouvait pas savoir à l'avance qu'il faudrait 24 tours : c'est un cas idéal pour `while`.
|
||
|
||
#### 5.4. Valider une saisie
|
||
|
||
Un usage extrêmement courant : redemander une valeur **tant qu'elle n'est pas valide**.
|
||
|
||
```python
|
||
note = float(input("Note sur 20 : "))
|
||
while note < 0 or note > 20:
|
||
print("Erreur : la note doit être comprise entre 0 et 20.")
|
||
note = float(input("Note sur 20 : "))
|
||
print(f"Note enregistrée : {note}")
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Note sur 20 : 25
|
||
Erreur : la note doit être comprise entre 0 et 20.
|
||
Note sur 20 : -3
|
||
Erreur : la note doit être comprise entre 0 et 20.
|
||
Note sur 20 : 14
|
||
Note enregistrée : 14.0
|
||
```
|
||
|
||
Ici, la boucle tourne 0, 1 ou 100 fois selon l'utilisateur : impossible à prévoir.
|
||
|
||
(Si l'utilisateur tape des lettres, `float()` plante encore : la gestion de ce cas avec `try`/`except` sera vue au [chapitre 08](08-erreurs-et-debogage.md).)
|
||
|
||
### 6. `break` et `continue`
|
||
|
||
Ces deux mots-clés permettent de modifier le déroulement normal d'une boucle. Ils s'utilisent presque toujours à l'intérieur d'un `if`.
|
||
|
||
#### 6.1. `break` : sortir immédiatement
|
||
|
||
`break` **interrompt** la boucle sur-le-champ. Python passe directement à la suite du programme, sans terminer le tour en cours ni faire les suivants.
|
||
|
||
```python
|
||
for nombre in range(1, 100):
|
||
if nombre * nombre > 50:
|
||
print(f"{nombre} est le premier nombre dont le carré dépasse 50.")
|
||
break
|
||
print(f"{nombre}² = {nombre * nombre}")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
1² = 1
|
||
2² = 4
|
||
3² = 9
|
||
4² = 16
|
||
5² = 25
|
||
6² = 36
|
||
7² = 49
|
||
8 est le premier nombre dont le carré dépasse 50.
|
||
```
|
||
|
||
#### 6.2. Le motif `while True` + `break`
|
||
|
||
`while True:` crée une boucle volontairement infinie, dont on sort avec `break` quand on le décide. C'est très lisible pour les menus et les programmes qui tournent jusqu'à ce que l'utilisateur dise stop :
|
||
|
||
```python
|
||
print("Tape des mots ('fin' pour arrêter).")
|
||
nb_mots = 0
|
||
while True:
|
||
mot = input("> ").strip()
|
||
if mot == "fin":
|
||
break
|
||
nb_mots += 1
|
||
print(f"'{mot}' contient {len(mot)} lettres.")
|
||
print(f"Tu as tapé {nb_mots} mots.")
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Tape des mots ('fin' pour arrêter).
|
||
> soleil
|
||
'soleil' contient 6 lettres.
|
||
> lune
|
||
'lune' contient 4 lettres.
|
||
> fin
|
||
Tu as tapé 2 mots.
|
||
```
|
||
|
||
Avec ce motif, la saisie de validation de la section 5.4 s'écrit sans dupliquer la ligne `input` :
|
||
|
||
```python
|
||
while True:
|
||
note = float(input("Note sur 20 : "))
|
||
if 0 <= note <= 20:
|
||
break
|
||
print("Erreur : la note doit être comprise entre 0 et 20.")
|
||
print(f"Note enregistrée : {note}")
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Note sur 20 : 25
|
||
Erreur : la note doit être comprise entre 0 et 20.
|
||
Note sur 20 : 14
|
||
Note enregistrée : 14.0
|
||
```
|
||
|
||
#### 6.3. `continue` : passer au tour suivant
|
||
|
||
`continue` **abandonne le tour en cours** et passe directement au tour suivant (la boucle, elle, continue).
|
||
|
||
```python
|
||
for nombre in range(1, 11):
|
||
if nombre % 3 == 0:
|
||
continue # on saute les multiples de 3
|
||
print(nombre, end=" ")
|
||
print()
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
1 2 4 5 7 8 10
|
||
```
|
||
|
||
Analogie : tu tries ton courrier. `continue`, c'est « cette lettre est une pub, je la jette et je passe à la suivante ». `break`, c'est « j'ai trouvé la lettre que je cherchais, j'arrête de trier ».
|
||
|
||
`continue` est utile pour **ignorer les cas sans intérêt** dès le début du bloc, plutôt que d'imbriquer tout le reste dans un `if` :
|
||
|
||
```python
|
||
phrase = "Il fait beau, allons-y !"
|
||
lettres = ""
|
||
for caractere in phrase:
|
||
if caractere in " ,!-":
|
||
continue
|
||
lettres += caractere
|
||
print(lettres)
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Ilfaitbeauallonsy
|
||
```
|
||
|
||
### 7. Le `else` de boucle
|
||
|
||
Voici une particularité de Python peu connue mais très pratique : une boucle `for` ou `while` peut avoir un bloc `else`. Il s'exécute **si la boucle s'est terminée normalement**, c'est-à-dire **sans avoir rencontré de `break`**.
|
||
|
||
C'est idéal pour les **recherches** : « j'ai cherché partout et je n'ai rien trouvé ».
|
||
|
||
```python
|
||
mot = "lynx"
|
||
for lettre in mot:
|
||
if lettre in "aeiou":
|
||
print(f"Première voyelle trouvée : {lettre}")
|
||
break
|
||
else:
|
||
print(f"Aucune voyelle (hors y) dans « {mot} ».")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Aucune voyelle (hors y) dans « lynx ».
|
||
```
|
||
|
||
Avec `mot = "python"`, le `break` aurait été déclenché sur le `o`, et le `else` n'aurait pas été exécuté.
|
||
|
||
L'exemple classique : tester si un nombre est **premier** (divisible seulement par 1 et par lui-même).
|
||
|
||
```python
|
||
nombre = 97
|
||
for diviseur in range(2, nombre):
|
||
if nombre % diviseur == 0:
|
||
print(f"{nombre} n'est pas premier : il est divisible par {diviseur}.")
|
||
break
|
||
else:
|
||
print(f"{nombre} est premier.")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
97 est premier.
|
||
```
|
||
|
||
Attention à l'indentation : le `else` est aligné avec le **`for`**, pas avec le `if`. Il appartient à la boucle.
|
||
|
||
> 💡 Pense le `else` de boucle comme un « sinon, si on n'a pas fait `break` » (*no break*). Sans lui, il faudrait une variable supplémentaire du style `trouve = False`, mise à `True` avant le `break`, puis testée après la boucle. Les deux approches sont correctes ; choisis celle qui te paraît la plus claire.
|
||
|
||
### 8. Les boucles imbriquées
|
||
|
||
Une boucle peut contenir une autre boucle. Pour **chaque tour** de la boucle extérieure, la boucle intérieure fait **tous** ses tours. Pense aux aiguilles d'une horloge : pour chaque heure, l'aiguille des minutes fait un tour complet.
|
||
|
||
```python
|
||
for heure in range(2):
|
||
for minute in range(0, 60, 20):
|
||
print(f"{heure}h{minute:02d}")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
0h00
|
||
0h20
|
||
0h40
|
||
1h00
|
||
1h20
|
||
1h40
|
||
```
|
||
|
||
2 tours extérieurs × 3 tours intérieurs = 6 affichages.
|
||
|
||
#### 8.1. Une table de multiplication complète
|
||
|
||
```python
|
||
for ligne in range(1, 6):
|
||
for colonne in range(1, 6):
|
||
print(f"{ligne * colonne:4}", end="")
|
||
print() # retour à la ligne à la fin de chaque ligne du tableau
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
1 2 3 4 5
|
||
2 4 6 8 10
|
||
3 6 9 12 15
|
||
4 8 12 16 20
|
||
5 10 15 20 25
|
||
```
|
||
|
||
Note l'emplacement du `print()` vide : il est dans la boucle extérieure (4 espaces), **après** la boucle intérieure. Il s'exécute donc une fois par ligne. `{...:4}` réserve 4 caractères pour chaque nombre, ce qui aligne les colonnes.
|
||
|
||
#### 8.2. Dessiner des motifs
|
||
|
||
Les boucles imbriquées permettent de dessiner des formes. Un triangle :
|
||
|
||
```python
|
||
hauteur = 5
|
||
for ligne in range(1, hauteur + 1):
|
||
for _ in range(ligne):
|
||
print("*", end="")
|
||
print()
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
*
|
||
**
|
||
***
|
||
****
|
||
*****
|
||
```
|
||
|
||
La boucle intérieure dépend de la variable de la boucle extérieure : à la ligne 1, une étoile ; à la ligne 2, deux étoiles, etc. Grâce à la répétition de chaînes (chapitre 01), on peut d'ailleurs remplacer la boucle intérieure par `print("*" * ligne)`.
|
||
|
||
#### 8.3. `break` dans des boucles imbriquées
|
||
|
||
`break` ne fait sortir que de la boucle **la plus proche** (la plus intérieure) :
|
||
|
||
```python
|
||
for a in range(1, 4):
|
||
for b in range(1, 4):
|
||
if b > a:
|
||
break # ne quitte que la boucle sur b
|
||
print(f"({a},{b})", end=" ")
|
||
print()
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
(1,1)
|
||
(2,1) (2,2)
|
||
(3,1) (3,2) (3,3)
|
||
```
|
||
|
||
Pour sortir de **toutes** les boucles d'un coup, la solution la plus propre sera de placer le code dans une fonction et d'utiliser `return` (voir le [chapitre 05](05-fonctions.md)).
|
||
|
||
### 9. Exemple complet : un menu interactif
|
||
|
||
Rassemblons tout : un programme qui tourne jusqu'à ce qu'on choisisse de quitter, avec `while True`, `match`, `break` et des boucles `for`.
|
||
|
||
```python
|
||
# Petit menu interactif
|
||
while True:
|
||
print()
|
||
print("=== MENU ===")
|
||
print("1. Table de multiplication")
|
||
print("2. Compte à rebours")
|
||
print("3. Quitter")
|
||
choix = input("Ton choix : ").strip()
|
||
|
||
match choix:
|
||
case "1":
|
||
n = int(input("Quelle table ? "))
|
||
for i in range(1, 11):
|
||
print(f"{n} x {i:2} = {n * i:3}")
|
||
case "2":
|
||
depart = int(input("Partir de combien ? "))
|
||
for i in range(depart, 0, -1):
|
||
print(i, end=" ")
|
||
print("Partez !")
|
||
case "3":
|
||
print("Au revoir !")
|
||
break
|
||
case _:
|
||
print("Choix invalide, recommence.")
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
|
||
=== MENU ===
|
||
1. Table de multiplication
|
||
2. Compte à rebours
|
||
3. Quitter
|
||
Ton choix : 2
|
||
Partir de combien ? 5
|
||
5 4 3 2 1 Partez !
|
||
|
||
=== MENU ===
|
||
1. Table de multiplication
|
||
2. Compte à rebours
|
||
3. Quitter
|
||
Ton choix : 7
|
||
Choix invalide, recommence.
|
||
|
||
=== MENU ===
|
||
1. Table de multiplication
|
||
2. Compte à rebours
|
||
3. Quitter
|
||
Ton choix : 3
|
||
Au revoir !
|
||
```
|
||
|
||
Remarque : le `break` placé dans un `case` fait bien sortir de la boucle `while` (un `match` n'est pas une boucle).
|
||
|
||
---
|
||
|
||
## Pièges fréquents
|
||
|
||
### Piège 1 — Oublier les deux-points
|
||
|
||
```python
|
||
for i in range(3)
|
||
print(i)
|
||
```
|
||
|
||
```text
|
||
File "boucles.py", line 1
|
||
for i in range(3)
|
||
^
|
||
SyntaxError: expected ':'
|
||
```
|
||
|
||
**Correction :** `for i in range(3):` et `while condition:` se terminent toujours par `:`.
|
||
|
||
### Piège 2 — Se tromper d'une unité avec `range()`
|
||
|
||
```python
|
||
# Objectif : afficher de 1 à 5
|
||
for i in range(5):
|
||
print(i, end=" ")
|
||
print()
|
||
for i in range(1, 5):
|
||
print(i, end=" ")
|
||
print()
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
0 1 2 3 4
|
||
1 2 3 4
|
||
```
|
||
|
||
Aucun des deux n'affiche de 1 à 5 ! C'est l'erreur « d'une unité » (*off-by-one*), un grand classique, même chez les pros.
|
||
|
||
**Correction :** `range(1, 6)`, ou plus généralement `range(1, n + 1)` pour aller de 1 à `n`.
|
||
|
||
### Piège 3 — Passer une chaîne ou un float à `range()`
|
||
|
||
```python
|
||
n = input("n : ")
|
||
for i in range(n):
|
||
print(i)
|
||
```
|
||
|
||
```text
|
||
n : 3
|
||
Traceback (most recent call last):
|
||
File "boucles.py", line 2, in <module>
|
||
for i in range(n):
|
||
~~~~~^^^
|
||
TypeError: 'str' object cannot be interpreted as an integer
|
||
```
|
||
|
||
Avec un float, comme `range(3.5)`, on obtient `TypeError: 'float' object cannot be interpreted as an integer`.
|
||
|
||
**Correction :** convertis avec `int()` : `n = int(input("n : "))`.
|
||
|
||
### Piège 4 — Boucler sur un nombre au lieu d'un `range`
|
||
|
||
```python
|
||
for i in 5:
|
||
print(i)
|
||
```
|
||
|
||
```text
|
||
Traceback (most recent call last):
|
||
File "boucles.py", line 1, in <module>
|
||
for i in 5:
|
||
^
|
||
TypeError: 'int' object is not iterable
|
||
```
|
||
|
||
Un nombre seul n'est pas « parcourable » (*iterable*) : il ne contient pas plusieurs éléments.
|
||
|
||
**Correction :** `for i in range(5):`.
|
||
|
||
### Piège 5 — La boucle `while` infinie
|
||
|
||
```python
|
||
compteur = 0
|
||
while compteur < 5:
|
||
print("Tour", compteur)
|
||
```
|
||
|
||
Le programme ne s'arrête jamais, car `compteur` vaut toujours 0.
|
||
|
||
**Correction :** appuie sur `Ctrl` + `C` pour l'arrêter, puis ajoute `compteur += 1` dans le bloc. Vérifie toujours que quelque chose, dans le bloc, finira par rendre la condition fausse (ou déclenchera un `break`).
|
||
|
||
### Piège 6 — Initialiser l'accumulateur dans la boucle
|
||
|
||
```python
|
||
for nombre in range(1, 4):
|
||
total = 0
|
||
total += nombre
|
||
print(total)
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
3
|
||
```
|
||
|
||
On attendait 6 (1 + 2 + 3), mais `total` est remis à 0 à chaque tour : à la fin, il ne contient que le dernier nombre.
|
||
|
||
**Correction :** place `total = 0` **avant** la boucle.
|
||
|
||
### Piège 7 — Une instruction mal indentée
|
||
|
||
```python
|
||
total = 0
|
||
for i in range(1, 4):
|
||
total += i
|
||
print(f"Total : {total}")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Total : 1
|
||
Total : 3
|
||
Total : 6
|
||
```
|
||
|
||
Si tu ne voulais afficher que le résultat final, le `print` est indenté d'un niveau de trop : il est exécuté à chaque tour.
|
||
|
||
**Correction :** désindente-le pour le sortir de la boucle :
|
||
|
||
```python
|
||
total = 0
|
||
for i in range(1, 4):
|
||
total += i
|
||
print(f"Total : {total}")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Total : 6
|
||
```
|
||
|
||
### Piège 8 — `break` en dehors d'une boucle
|
||
|
||
```python
|
||
reponse = "non"
|
||
if reponse == "non":
|
||
break
|
||
```
|
||
|
||
```text
|
||
File "boucles.py", line 3
|
||
break
|
||
^^^^^
|
||
SyntaxError: 'break' outside loop
|
||
```
|
||
|
||
`break` et `continue` n'ont de sens qu'à l'intérieur d'une boucle `for` ou `while`. (Pour arrêter tout le programme, il existe `exit()`, mais on préfère généralement organiser son code pour qu'il se termine naturellement.)
|
||
|
||
---
|
||
|
||
## Exercices
|
||
|
||
### Exercice 1 ⭐ — Table de multiplication
|
||
|
||
Écris un programme qui demande un nombre et affiche sa table de multiplication de 1 à 10, avec les résultats alignés à droite.
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Quelle table ? 7
|
||
7 x 1 = 7
|
||
7 x 2 = 14
|
||
7 x 3 = 21
|
||
7 x 4 = 28
|
||
7 x 5 = 35
|
||
7 x 6 = 42
|
||
7 x 7 = 49
|
||
7 x 8 = 56
|
||
7 x 9 = 63
|
||
7 x 10 = 70
|
||
```
|
||
|
||
### Exercice 2 ⭐ — Somme et nombres pairs
|
||
|
||
Écris un programme qui demande un entier `n` positif, puis affiche :
|
||
|
||
1. la somme des entiers de 1 à `n` ;
|
||
2. la liste des nombres pairs de 2 à `n` sur une seule ligne, séparés par des espaces ;
|
||
3. combien il y a de multiples de 7 entre 1 et `n`.
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
n = 20
|
||
Somme de 1 à 20 : 210
|
||
Nombres pairs : 2 4 6 8 10 12 14 16 18 20
|
||
Multiples de 7 : 2
|
||
```
|
||
|
||
### Exercice 3 ⭐ — Analyse d'un mot
|
||
|
||
Écris un programme qui demande un mot et affiche le nombre de voyelles, le nombre de consonnes, et le mot écrit avec des tirets entre les lettres. On considère que les voyelles sont `aeiouy` (en minuscules ou majuscules) et que tout autre caractère alphabétique est une consonne (indice : `caractere.isalpha()` renvoie `True` si le caractère est une lettre).
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Un mot : Python
|
||
Voyelles : 2
|
||
Consonnes : 4
|
||
Épelé : P-y-t-h-o-n
|
||
```
|
||
|
||
### Exercice 4 ⭐⭐ — La saisie obstinée
|
||
|
||
Écris un programme qui demande un nombre entier entre 1 et 10. Tant que la saisie n'est pas dans l'intervalle, il affiche un message d'erreur et redemande. Au bout de **3 essais ratés**, il abandonne avec « Trop d'erreurs, au revoir. ». En cas de succès, il affiche le nombre et le nombre d'essais utilisés.
|
||
|
||
Exemples d'exécution :
|
||
|
||
```text
|
||
Un nombre entre 1 et 10 : 15
|
||
Hors limites ! (2 essai(s) restant(s))
|
||
Un nombre entre 1 et 10 : 0
|
||
Hors limites ! (1 essai(s) restant(s))
|
||
Un nombre entre 1 et 10 : 4
|
||
Merci ! Tu as choisi 4 en 3 essai(s).
|
||
```
|
||
|
||
```text
|
||
Un nombre entre 1 et 10 : 11
|
||
Hors limites ! (2 essai(s) restant(s))
|
||
Un nombre entre 1 et 10 : 12
|
||
Hors limites ! (1 essai(s) restant(s))
|
||
Un nombre entre 1 et 10 : 13
|
||
Trop d'erreurs, au revoir.
|
||
```
|
||
|
||
### Exercice 5 ⭐⭐ — FizzBuzz
|
||
|
||
Le grand classique des entretiens d'embauche ! Affiche les nombres de 1 à 20, mais :
|
||
|
||
- remplace les multiples de 3 par `Fizz` ;
|
||
- remplace les multiples de 5 par `Buzz` ;
|
||
- remplace les multiples de 3 **et** de 5 par `FizzBuzz`.
|
||
|
||
Le programme affiche un élément par ligne. Voici la suite attendue, écrite ici sur une seule ligne pour gagner de la place :
|
||
|
||
```text
|
||
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz
|
||
```
|
||
|
||
### Exercice 6 ⭐⭐ — Nombres premiers
|
||
|
||
Écris un programme qui demande un entier `limite` et affiche tous les nombres premiers de 2 à `limite`, sur une ligne, puis combien il y en a. Utilise deux boucles imbriquées et le `else` de boucle.
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Limite : 50
|
||
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
|
||
Il y a 15 nombres premiers jusqu'à 50.
|
||
```
|
||
|
||
Bonus : il suffit de tester les diviseurs jusqu'à la racine carrée du nombre (`diviseur * diviseur <= nombre`). Comment l'intégrer ?
|
||
|
||
### Exercice 7 ⭐⭐⭐ — Le sapin de Noël
|
||
|
||
Écris un programme qui demande une hauteur et dessine un sapin centré, avec un tronc.
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Hauteur du sapin : 5
|
||
*
|
||
***
|
||
*****
|
||
*******
|
||
*********
|
||
|
|
||
```
|
||
|
||
Indice : à la ligne numéro `i` (en commençant à 0), il y a `hauteur - 1 - i` espaces puis `2 * i + 1` étoiles.
|
||
|
||
### Exercice 8 ⭐⭐⭐ — La suite de Syracuse
|
||
|
||
On part d'un entier positif `n`. À chaque étape : si `n` est pair, on le divise par 2 ; sinon, on le multiplie par 3 et on ajoute 1. Une célèbre conjecture (jamais démontrée !) affirme qu'on finit toujours par atteindre 1.
|
||
|
||
Écris un programme qui demande `n`, affiche toute la suite jusqu'à 1, puis le nombre d'étapes et la valeur maximale atteinte (« l'altitude »).
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Nombre de départ : 6
|
||
6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1
|
||
Étapes : 8
|
||
Altitude maximale : 16
|
||
```
|
||
|
||
Essaie ensuite avec 27 : tu seras surpris !
|
||
|
||
---
|
||
|
||
## Mini-projet
|
||
|
||
### Le jeu du nombre mystère
|
||
|
||
**Objectif :** programmer le jeu classique où l'ordinateur choisit un nombre au hasard et où le joueur doit le deviner, guidé par des indices « plus grand » / « plus petit ».
|
||
|
||
Pour tirer un nombre au hasard, on utilise le **module** `random`. Les modules seront expliqués au [chapitre 09](09-modules-bibliotheque-standard.md) ; pour l'instant, recopie simplement ces deux lignes :
|
||
|
||
```python
|
||
import random
|
||
|
||
nombre_mystere = random.randint(1, 100) # entier au hasard entre 1 et 100 inclus
|
||
```
|
||
|
||
(La ligne `import random` se place tout en haut du fichier.)
|
||
|
||
**Cahier des charges :**
|
||
|
||
1. Le programme affiche les règles, puis tire un nombre entre 1 et 100.
|
||
2. Le joueur a **7 essais** au maximum. À chaque essai, le programme affiche le numéro de l'essai, demande une proposition et répond « C'est plus ! », « C'est moins ! » ou annonce la victoire avec le nombre d'essais utilisés.
|
||
3. Une proposition hors de l'intervalle 1–100 affiche un avertissement et **ne compte pas** comme un essai.
|
||
4. Si le joueur épuise ses essais, le programme révèle le nombre mystère.
|
||
5. À la fin d'une partie, le programme demande « Rejouer (o/n) ? » et recommence une nouvelle partie tant que le joueur répond `o`.
|
||
6. Quand le joueur arrête, le programme affiche le nombre de parties jouées, le nombre de victoires et le meilleur score (le plus petit nombre d'essais pour une victoire), ou « aucune victoire » s'il n'a jamais gagné.
|
||
|
||
**Exemple d'exécution :**
|
||
|
||
```text
|
||
=== LE NOMBRE MYSTÈRE ===
|
||
Je pense à un nombre entre 1 et 100. Tu as 7 essais.
|
||
|
||
Essai 1/7 : 50
|
||
C'est moins !
|
||
Essai 2/7 : 25
|
||
C'est plus !
|
||
Essai 3/7 : 150
|
||
Entre 1 et 100, s'il te plaît (essai non compté).
|
||
Essai 3/7 : 37
|
||
Bravo ! Trouvé en 3 essai(s).
|
||
Rejouer (o/n) ? n
|
||
|
||
Parties jouées : 1
|
||
Victoires : 1
|
||
Meilleur score : 3 essai(s)
|
||
```
|
||
|
||
**Étapes suggérées :**
|
||
|
||
1. Commence par une seule partie, sans limite d'essais : une boucle `while True` qui s'arrête (`break`) quand le joueur trouve.
|
||
2. Ajoute le compteur d'essais et la limite de 7. Le `else` de boucle (ou une variable `gagne`) permet de détecter la défaite.
|
||
3. Ajoute le contrôle de l'intervalle avec `continue`.
|
||
4. Enveloppe le tout dans une deuxième boucle, extérieure, pour rejouer. Attention à bien réinitialiser le nombre mystère et le compteur d'essais à chaque nouvelle partie.
|
||
5. Ajoute les statistiques, initialisées **avant** la boucle extérieure.
|
||
6. Pour tester facilement, affiche temporairement le nombre mystère au début de la partie (et pense à retirer cette ligne ensuite !).
|
||
|
||
**Pistes d'amélioration :**
|
||
|
||
- Propose trois niveaux de difficulté (1–50 en 10 essais, 1–100 en 7 essais, 1–1000 en 10 essais) avec `match`.
|
||
- Affiche « Tu chauffes ! » quand le joueur est à moins de 5 du nombre.
|
||
- Inverse les rôles : c'est toi qui penses à un nombre, et l'ordinateur doit le trouver en répondant à tes indications. Avec la bonne stratégie (couper l'intervalle en deux à chaque fois, la « recherche dichotomique »), il trouve toujours en 7 essais au plus !
|
||
- Quand tu auras vu le chapitre 07, enregistre le meilleur score dans un fichier pour le conserver d'une session à l'autre.
|
||
|
||
---
|
||
|
||
## Corrigés
|
||
|
||
<details>
|
||
<summary>Corrigé de l'exercice 1</summary>
|
||
|
||
```python
|
||
# Table de multiplication
|
||
n = int(input("Quelle table ? "))
|
||
for i in range(1, 11):
|
||
print(f"{n:2} x {i:2} = {n * i:2}")
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Quelle table ? 7
|
||
7 x 1 = 7
|
||
7 x 2 = 14
|
||
7 x 3 = 21
|
||
7 x 4 = 28
|
||
7 x 5 = 35
|
||
7 x 6 = 42
|
||
7 x 7 = 49
|
||
7 x 8 = 56
|
||
7 x 9 = 63
|
||
7 x 10 = 70
|
||
```
|
||
|
||
`range(1, 11)` va bien de 1 à 10. `{i:2}` réserve 2 caractères : les nombres (alignés à droite par défaut) forment des colonnes propres.
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary>Corrigé de l'exercice 2</summary>
|
||
|
||
```python
|
||
# Somme, nombres pairs et multiples de 7
|
||
n = int(input("n = "))
|
||
|
||
# 1. Accumulateur
|
||
somme = 0
|
||
for nombre in range(1, n + 1):
|
||
somme += nombre
|
||
print(f"Somme de 1 à {n} : {somme}")
|
||
|
||
# 2. Nombres pairs : range avec un pas de 2
|
||
print("Nombres pairs :", end="")
|
||
for nombre in range(2, n + 1, 2):
|
||
print(f" {nombre}", end="")
|
||
print()
|
||
|
||
# 3. Compteur
|
||
nb_multiples_7 = 0
|
||
for nombre in range(1, n + 1):
|
||
if nombre % 7 == 0:
|
||
nb_multiples_7 += 1
|
||
print(f"Multiples de 7 : {nb_multiples_7}")
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
n = 20
|
||
Somme de 1 à 20 : 210
|
||
Nombres pairs : 2 4 6 8 10 12 14 16 18 20
|
||
Multiples de 7 : 2
|
||
```
|
||
|
||
Pour les pairs, le pas de 2 évite de tester chaque nombre avec `% 2`. Pour les multiples de 7, on aurait pu faire de même avec `range(7, n + 1, 7)`, ou même calculer directement `n // 7` ! Il y a souvent plusieurs solutions.
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary>Corrigé de l'exercice 3</summary>
|
||
|
||
```python
|
||
# Analyse d'un mot
|
||
mot = input("Un mot : ")
|
||
|
||
nb_voyelles = 0
|
||
nb_consonnes = 0
|
||
epele = ""
|
||
|
||
for caractere in mot:
|
||
if caractere.lower() in "aeiouy":
|
||
nb_voyelles += 1
|
||
elif caractere.isalpha():
|
||
nb_consonnes += 1
|
||
|
||
if epele == "":
|
||
epele = caractere
|
||
else:
|
||
epele += "-" + caractere
|
||
|
||
print(f"Voyelles : {nb_voyelles}")
|
||
print(f"Consonnes : {nb_consonnes}")
|
||
print(f"Épelé : {epele}")
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Un mot : Python
|
||
Voyelles : 2
|
||
Consonnes : 4
|
||
Épelé : P-y-t-h-o-n
|
||
```
|
||
|
||
L'ordre `if`/`elif` est important : une voyelle est aussi une lettre (`isalpha()` vaut `True`), donc on teste d'abord les voyelles. Pour l'épellation, on n'ajoute le tiret qu'à partir de la deuxième lettre, pour éviter un tiret au début. (Au chapitre 06, tu découvriras `"-".join(mot)`, qui fait la même chose en une instruction.)
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary>Corrigé de l'exercice 4</summary>
|
||
|
||
```python
|
||
# Saisie validée avec un nombre d'essais limité
|
||
essais_max = 3
|
||
essais = 0
|
||
|
||
while essais < essais_max:
|
||
nombre = int(input("Un nombre entre 1 et 10 : "))
|
||
essais += 1
|
||
if 1 <= nombre <= 10:
|
||
print(f"Merci ! Tu as choisi {nombre} en {essais} essai(s).")
|
||
break
|
||
restants = essais_max - essais
|
||
if restants > 0:
|
||
print(f"Hors limites ! ({restants} essai(s) restant(s))")
|
||
else:
|
||
print("Trop d'erreurs, au revoir.")
|
||
```
|
||
|
||
Exemples d'exécution :
|
||
|
||
```text
|
||
Un nombre entre 1 et 10 : 15
|
||
Hors limites ! (2 essai(s) restant(s))
|
||
Un nombre entre 1 et 10 : 0
|
||
Hors limites ! (1 essai(s) restant(s))
|
||
Un nombre entre 1 et 10 : 4
|
||
Merci ! Tu as choisi 4 en 3 essai(s).
|
||
```
|
||
|
||
```text
|
||
Un nombre entre 1 et 10 : 11
|
||
Hors limites ! (2 essai(s) restant(s))
|
||
Un nombre entre 1 et 10 : 12
|
||
Hors limites ! (1 essai(s) restant(s))
|
||
Un nombre entre 1 et 10 : 13
|
||
Trop d'erreurs, au revoir.
|
||
```
|
||
|
||
Le `else` de la boucle `while` s'exécute quand la condition `essais < essais_max` devient fausse, c'est-à-dire quand on a épuisé les essais **sans** passer par le `break`. On n'affiche le message « Hors limites » que s'il reste des essais, pour ne pas écrire « 0 essai(s) restant(s) » juste avant « Trop d'erreurs ».
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary>Corrigé de l'exercice 5</summary>
|
||
|
||
```python
|
||
# FizzBuzz de 1 à 20
|
||
for nombre in range(1, 21):
|
||
if nombre % 15 == 0: # multiple de 3 ET de 5
|
||
print("FizzBuzz")
|
||
elif nombre % 3 == 0:
|
||
print("Fizz")
|
||
elif nombre % 5 == 0:
|
||
print("Buzz")
|
||
else:
|
||
print(nombre)
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
1
|
||
2
|
||
Fizz
|
||
4
|
||
Buzz
|
||
Fizz
|
||
7
|
||
8
|
||
Fizz
|
||
Buzz
|
||
11
|
||
Fizz
|
||
13
|
||
14
|
||
FizzBuzz
|
||
16
|
||
17
|
||
Fizz
|
||
19
|
||
Buzz
|
||
```
|
||
|
||
Le piège : tester `% 15` (ou `% 3 == 0 and % 5 == 0`) **en premier**. Si on testait d'abord `% 3`, 15 afficherait « Fizz » et le cas « FizzBuzz » ne serait jamais atteint (rappel du chapitre 02 : avec `elif`, la première condition vraie gagne).
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary>Corrigé de l'exercice 6</summary>
|
||
|
||
```python
|
||
# Nombres premiers jusqu'à une limite
|
||
limite = int(input("Limite : "))
|
||
|
||
nb_premiers = 0
|
||
for nombre in range(2, limite + 1):
|
||
for diviseur in range(2, nombre):
|
||
if nombre % diviseur == 0:
|
||
break # un diviseur trouvé : pas premier
|
||
else:
|
||
# aucun break : aucun diviseur trouvé, le nombre est premier
|
||
if nb_premiers > 0:
|
||
print(" ", end="")
|
||
print(nombre, end="")
|
||
nb_premiers += 1
|
||
print()
|
||
print(f"Il y a {nb_premiers} nombres premiers jusqu'à {limite}.")
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Limite : 50
|
||
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
|
||
Il y a 15 nombres premiers jusqu'à 50.
|
||
```
|
||
|
||
Pour `nombre = 2`, `range(2, 2)` est vide : la boucle intérieure ne fait aucun tour, donc aucun `break`, et le `else` s'exécute : 2 est bien premier.
|
||
|
||
**Bonus :** si `nombre` a un diviseur, il en a forcément un inférieur ou égal à sa racine carrée. On peut donc arrêter la recherche plus tôt avec un `while` :
|
||
|
||
```python
|
||
limite = 50
|
||
nb_premiers = 0
|
||
for nombre in range(2, limite + 1):
|
||
diviseur = 2
|
||
while diviseur * diviseur <= nombre:
|
||
if nombre % diviseur == 0:
|
||
break
|
||
diviseur += 1
|
||
else:
|
||
nb_premiers += 1
|
||
print(f"Il y a {nb_premiers} nombres premiers jusqu'à {limite}.")
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
Il y a 15 nombres premiers jusqu'à 50.
|
||
```
|
||
|
||
Pour 97, on teste alors 8 diviseurs au lieu de 95 : sur de grands nombres, la différence est énorme.
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary>Corrigé de l'exercice 7</summary>
|
||
|
||
```python
|
||
# Sapin de Noël
|
||
hauteur = int(input("Hauteur du sapin : "))
|
||
|
||
for i in range(hauteur):
|
||
espaces = hauteur - 1 - i
|
||
etoiles = 2 * i + 1
|
||
print(" " * espaces + "*" * etoiles)
|
||
|
||
# Le tronc, centré sous la pointe
|
||
print(" " * (hauteur - 1) + "|")
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Hauteur du sapin : 5
|
||
*
|
||
***
|
||
*****
|
||
*******
|
||
*********
|
||
|
|
||
```
|
||
|
||
Ici, la répétition de chaînes (`" " * espaces`) remplace une boucle intérieure. Version avec de vraies boucles imbriquées, pour s'entraîner :
|
||
|
||
```python
|
||
hauteur = 3
|
||
for i in range(hauteur):
|
||
for _ in range(hauteur - 1 - i):
|
||
print(" ", end="")
|
||
for _ in range(2 * i + 1):
|
||
print("*", end="")
|
||
print()
|
||
```
|
||
|
||
Sortie :
|
||
|
||
```text
|
||
*
|
||
***
|
||
*****
|
||
```
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary>Corrigé de l'exercice 8</summary>
|
||
|
||
```python
|
||
# Suite de Syracuse
|
||
n = int(input("Nombre de départ : "))
|
||
|
||
etapes = 0
|
||
altitude = n
|
||
suite = str(n)
|
||
|
||
while n != 1:
|
||
if n % 2 == 0:
|
||
n = n // 2 # division entière : n reste un int
|
||
else:
|
||
n = 3 * n + 1
|
||
etapes += 1
|
||
if n > altitude:
|
||
altitude = n
|
||
suite += f" → {n}"
|
||
|
||
print(suite)
|
||
print(f"Étapes : {etapes}")
|
||
print(f"Altitude maximale : {altitude}")
|
||
```
|
||
|
||
Exemple d'exécution :
|
||
|
||
```text
|
||
Nombre de départ : 6
|
||
6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1
|
||
Étapes : 8
|
||
Altitude maximale : 16
|
||
```
|
||
|
||
Avec 27, la suite fait 111 étapes et monte jusqu'à 9232 avant de redescendre ! C'est un cas idéal pour `while` : impossible de savoir à l'avance combien de tours seront nécessaires. On utilise `//` et pas `/`, sinon `n` deviendrait un float (`3.0`, `10.0`…). On combine trois motifs : un compteur (`etapes`), une recherche de maximum (`altitude`) et un accumulateur de chaîne (`suite`).
|
||
|
||
</details>
|
||
|
||
<details>
|
||
<summary>Corrigé du mini-projet</summary>
|
||
|
||
```python
|
||
# nombre_mystere.py — Jeu du nombre mystère
|
||
import random
|
||
|
||
ESSAIS_MAX = 7
|
||
|
||
print("=== LE NOMBRE MYSTÈRE ===")
|
||
|
||
# Statistiques sur l'ensemble des parties
|
||
nb_parties = 0
|
||
nb_victoires = 0
|
||
meilleur_score = 0 # 0 signifie « pas encore de victoire »
|
||
|
||
while True: # boucle des parties
|
||
nombre_mystere = random.randint(1, 100)
|
||
nb_parties += 1
|
||
essai = 1
|
||
print(f"Je pense à un nombre entre 1 et 100. Tu as {ESSAIS_MAX} essais.")
|
||
print()
|
||
|
||
while essai <= ESSAIS_MAX: # boucle des essais d'une partie
|
||
proposition = int(input(f"Essai {essai}/{ESSAIS_MAX} : "))
|
||
|
||
if proposition < 1 or proposition > 100:
|
||
print("Entre 1 et 100, s'il te plaît (essai non compté).")
|
||
continue # on redemande sans incrémenter essai
|
||
|
||
if proposition < nombre_mystere:
|
||
print("C'est plus !")
|
||
elif proposition > nombre_mystere:
|
||
print("C'est moins !")
|
||
else:
|
||
print(f"Bravo ! Trouvé en {essai} essai(s).")
|
||
nb_victoires += 1
|
||
if meilleur_score == 0 or essai < meilleur_score:
|
||
meilleur_score = essai
|
||
break
|
||
essai += 1
|
||
else:
|
||
# la boucle s'est terminée sans break : le joueur a perdu
|
||
print(f"Perdu ! Le nombre mystère était {nombre_mystere}.")
|
||
|
||
rejouer = input("Rejouer (o/n) ? ").strip().lower()
|
||
if rejouer != "o":
|
||
break
|
||
print()
|
||
|
||
# Bilan
|
||
print()
|
||
print(f"Parties jouées : {nb_parties}")
|
||
print(f"Victoires : {nb_victoires}")
|
||
if meilleur_score == 0:
|
||
print("Meilleur score : aucune victoire")
|
||
else:
|
||
print(f"Meilleur score : {meilleur_score} essai(s)")
|
||
```
|
||
|
||
Exemple d'exécution (le nombre tiré était 37) :
|
||
|
||
```text
|
||
=== LE NOMBRE MYSTÈRE ===
|
||
Je pense à un nombre entre 1 et 100. Tu as 7 essais.
|
||
|
||
Essai 1/7 : 50
|
||
C'est moins !
|
||
Essai 2/7 : 25
|
||
C'est plus !
|
||
Essai 3/7 : 150
|
||
Entre 1 et 100, s'il te plaît (essai non compté).
|
||
Essai 3/7 : 37
|
||
Bravo ! Trouvé en 3 essai(s).
|
||
Rejouer (o/n) ? n
|
||
|
||
Parties jouées : 1
|
||
Victoires : 1
|
||
Meilleur score : 3 essai(s)
|
||
```
|
||
|
||
Points clés de la solution :
|
||
|
||
- **Deux boucles imbriquées** : la boucle extérieure gère les parties, la boucle intérieure les essais d'une partie. Le nombre mystère et le compteur `essai` sont réinitialisés **au début de chaque partie**, les statistiques **une seule fois avant** toutes les parties.
|
||
- **`continue`** pour une saisie hors limites : on repart au début de la boucle **sans** exécuter `essai += 1`, donc l'essai n'est pas compté.
|
||
- **`break`** en cas de victoire, et le **`else` de boucle** pour détecter la défaite.
|
||
- Le second `break`, après « Rejouer », sort de la boucle **extérieure** (c'est la boucle la plus proche de lui).
|
||
- `ESSAIS_MAX` est écrit en majuscules : c'est la convention pour une **constante**, une valeur qu'on ne modifie jamais pendant le programme. La changer ici modifie tout le jeu.
|
||
- Pour la variante où l'ordinateur devine, garde deux variables `borne_min` et `borne_max`, propose `(borne_min + borne_max) // 2`, et resserre l'intervalle selon la réponse.
|
||
|
||
</details>
|
||
|
||
---
|
||
|
||
## Récapitulatif
|
||
|
||
**Points clés :**
|
||
|
||
- `for variable in iterable:` répète le bloc pour chaque élément d'une plage de nombres, d'une chaîne ou d'une liste.
|
||
- `range(fin)`, `range(debut, fin)`, `range(debut, fin, pas)` : la fin est **toujours exclue**. De 1 à `n` inclus : `range(1, n + 1)`.
|
||
- `while condition:` répète tant que la condition est vraie. Il faut initialiser la variable testée avant, et la modifier dans le bloc. `Ctrl` + `C` arrête une boucle infinie.
|
||
- `for` quand on sait sur quoi boucler ; `while` quand le nombre de tours est inconnu.
|
||
- `break` sort immédiatement de la boucle (la plus proche) ; `continue` passe au tour suivant.
|
||
- Le `else` d'une boucle s'exécute si elle se termine **sans `break`** : idéal pour les recherches.
|
||
- Motifs à connaître : **accumulateur** (`total = 0` avant, `total += x` dedans), **compteur**, **recherche de maximum**, **saisie validée** avec `while True` + `break`.
|
||
- Dans des boucles imbriquées, la boucle intérieure fait tous ses tours pour chaque tour de la boucle extérieure.
|
||
- L'indentation décide de ce qui est répété : une ligne mal indentée est exécutée trop souvent, ou pas assez.
|
||
|
||
**Checklist de validation :**
|
||
|
||
- [ ] Je sais afficher les nombres de 1 à `n`, de `n` à 1 et de 2 en 2 avec `range()`.
|
||
- [ ] Je sais parcourir les lettres d'un mot et les éléments d'une liste simple avec `for`.
|
||
- [ ] Je sais écrire un `while` qui se termine toujours, et arrêter une boucle infinie.
|
||
- [ ] Je sais calculer une somme, compter des éléments et trouver un maximum avec une boucle.
|
||
- [ ] Je sais redemander une saisie tant qu'elle est invalide.
|
||
- [ ] Je sais expliquer la différence entre `break` et `continue`, et quand le `else` d'une boucle s'exécute.
|
||
- [ ] J'ai programmé le jeu du nombre mystère avec la possibilité de rejouer.
|
||
|
||
---
|
||
|
||
[← Chapitre précédent](02-conditions.md) | [Sommaire](README.md) | [Chapitre suivant →](04-structures-de-donnees.md)
|