Les boucles while#
Motivation: répéter ou ne pas se répéter?#
Un ordinateur est capable d’effectuer des tâches répétitives très rapidement et sans s’ennuyer. Pas nous! Aussi le principe suivant de programmation va revenir comme un leitmotiv tout au long de ce cours :
«Ne te répètes pas».
En anglais cela donne l’acronyme mnémotechnique «DRY»:
Don’t Repeat Yourself
Nous avions vu un premier exemple de ce principe dans la fiche on regarde devant soi avec ce labyrinthe:
from laby.global_fr import *
Laby(niveau="2a")
La proposition de solution ci-dessous n’est pas satisfaisante: elle est longue et répétitive. De plus, elle n’est pas générale: il faut compter le nombre de cases du couloir et écrire une solution différente selon le cas.
debut();
avance();
avance();
avance();
avance();
avance();
avance();
avance();
avance();
avance();
avance();
avance();
ouvre();
Pour palier cela, nous avions alors découvert un premier outil pour éviter les
répétitions dans le code: la boucle while. Cet outil nous a permis de programmer la
consigne: «Tant que c’est vide devant toi, avance!».
Dans cette fiche, nous allons introduire formellement les boucles while. C’est un
premier exemple d”
instructions itératives (iterative instruction): ces
instructions permettent de répéter un certain nombre de fois l’exécution d’un bloc
d’instructions sous certaines conditions. De façon imagée, on appelle
boucle (loop) cette méthode permettant de répéter l’exécution d’un bloc
d’instructions.
La boucle while : syntaxe et sémantique#
Une boucle while s’écrit de la façon suivante:
Syntaxe
while condition:
bloc d instructions
et est exécutée par Python comme suit:
Sémantique
Évaluation de la condition
Si sa valeur est «Vrai» :
Exécution du bloc d’instructions
On recommence en 1.
Une boucle while commence donc toujours par une condition. Python vérifie si celle-ci
est vraie: si elle ne l’est pas, on ne rentre même pas dans la boucle ! En revanche, si
la condition est vraie, Python exécute le bloc d’instructions qui est juste en dessous.
Une fois cette première itération effectuée, Python vérifie si la condition est encore
vraie, et si oui exécute une nouvelle fois le bloc d’instructions; et ainsi de suite,
jusqu’à ce que la condition ne soit plus vérifiée. Alors, l’exécution sort de la boucle
et la suite du programme prend le relais.
Premiers exemples#
Voici le programme que nous avions écrit pour le couloir:
from laby.global_fr import *
Laby(niveau="2a")
debut();
while regarde() == Vide:
avance()
ouvre();
Notez la condition regarde() == Vide et le bloc d’instruction réduit à une seule
instruction avance(). Notez aussi l’indentation de quatre espaces devant avance(),
tandis que ouvre() – qui vient après la boucle – n’est pas indenté.
Exécutez maintenant la cellule suivante et observez le résultat :
n = 1
while n <= 5:
print(n)
n = n + 1
1
2
3
4
5
Nous avons réalisé un programme permettant de compter de 1 à 5. Quelle est la condition? Quelles sont les instructions dans le bloc d’instructions?
Exercice 1 : première boucle while#
Exécutez la cellule suivante :
n = 1
a = 5
while n <= a:
print(n)
n = n + 1
1
2
3
4
5
Modifiez la valeur de
aci-dessus pour obtenir :
Exactement 1 affichage
Exactement 0 affichage
Exactement 4 affichages
Exercice 2 : Panique dans le noir#
Notre fourmi est perdue dans un labyrinthe plongé dans le noir. Elle panique! Avant chaque pas, elle tourne aléatoirement vers la gauche ou vers la droite. Va-t-elle trouver la sortie?
À vous de le découvrir en programmant ce comportement de la fourmi!
Pour simuler le noir, nous avons écrit une fonction qui place la porte de façon aléatoire. Exécutez les deux cellules suivantes. À chaque création du labyrinthe, la porte est placée dans un endroit différent. Le même programme doit toujours fonctionner !
Indications:
Vous pouvez utiliser la condition
regarde() != Sortie;Pour tirer à pile ou face si la fourmi va tourner à gauche ou à droite, vous pouvez utiliser la fonction
random.randint(0,1)qui renvoie aléatoirement 0 ou 1.
import random
def porteAleatoire():
s = "o o o o o o o\n"
s += "o → . . . . o\n"
ligne = random.randint(0, 3)
col = random.randint(0, 4)
for i in range(4):
s += "o "
for j in range(5):
if ( i == ligne and j == col ):
s += "x "
else:
s += ". "
s += "o\n"
s += "o o o o o o o\n"
return s
from laby.global_fr import *
Laby(carte = porteAleatoire())
debut()
### BEGIN SOLUTION
while ( regarde() != Sortie ):
if ( random.randint(0,1) == 0 ):
gauche()
else:
droite()
avance()
ouvre()
### END SOLUTION
Exercice 3 : division euclidienne par soustraction#
Observez les cellules suivantes, puis dans la cellule suivante, affectez des valeurs à \(b\) et \(c\) avec \(b > c\) de sorte que, à la fin de la boucle, on ait \(b=3\) :
### BEGIN SOLUTION
b = 18;
c = 5;
### END SOLUTION
while ( b >= c ):
b = b - c
b
3
Test automatique (ne doit rien afficher) :
assert( b == 3 )
Modifiez la cellule ci-dessous en introduisant une variable \(k\) qui compte le nombre d’exécutions de la boucle;
Affectez à \(b\) et \(c\) avant la boucle des valeurs de sorte que, à la fin de la boucle, on ait \(b=3\) et \(k=5\).
### BEGIN SOLUTION
b = 23;
c = 4;
k = 0;
### END SOLUTION
while ( b >= c ):
b = b - c
### BEGIN SOLUTION
k = k + 1
### END SOLUTION
b # doit afficher 3
3
k # doit afficher 5
5
Tests automatiques :
assert( b == 3 )
assert( k == 5 )
Exercice 4 : suite de Syracuse#
Exécutez, sans la modifier, la cellule ci-dessous puis trois fois la deuxième cellule :
d = 5
if ( d % 2 == 0):
d = d // 2;
else:
d = 3 * d + 1
print(d)
16
Vous noterez que lorsque le nombre \(d\) est pair, on le divise par 2 (avec l’opérateur de
division entière //). Lorsqu’il est impair, on le multiplie par 3 et on ajoute 1.
Réécrivez la cellule en la complétant pour que l’on répète cette action tant que le nombre \(d\) est supérieur à 1. On veut afficher la valeur de \(d\) à chaque fois. Si votre code est correct, il doit afficher 16 8 4 2 1 (en partant de d=5).
### BEGIN SOLUTION
d = 5
while d > 1:
if d % 2 == 0:
d = d // 2;
else:
d = 3 * d + 1
print(d)
### END SOLUTION
16
8
4
2
1
Exercice 5#
Observez la fonction suivante et essayez de comprendre ce qu’elle calcule en lisant le code et en essayant plusieurs valeurs d’appel :
def mystere(n):
k = 1
while k * k < n:
k = k + 1
return k
mystere(5)
3
Déterminez une valeur de n tel que la valeur renvoyée soit 6 :
n = 0
### BEGIN SOLUTION
n = 36
### END SOLUTION
mystere(n)
6
assert( mystere(n) == 6 )
La boucle while : d’autres exemples#
Cas particulier : condition toujours fausse#
Si la valeur de la condition est fausse dès le départ, alors le bloc d’instructions ne sera jamais exécuté!
n = 1
while n <= 0:
print(n) ## Affiche la valeur de n
n = n + 1;
Cas particulier : condition toujours vraie#
Si la valeur de la condition est toujours vraie, alors le bloc d’instructions sera exécuté indéfiniment!
Avertissement
Attention ⚠️ L’exemple suivant ne va pas s’arrêter! Il faudra redémarrer le noyau (menu Noyau) ⚠️
n = 1
while True:
print(n) # Affiche la valeur de n
n = n + 1
Erreur classique : fin de boucle#
Que vaut \(n\) à la fin du programme suivant?
n = 1
while n <= 5:
n = n + 1
print(n)
6
À retenir :
On sort de la boucle quand la condition vaut «Faux»; le compteur est donc «un cran trop loin».
Bilan#
Vous pouvez à présent vous entraîner sur des exercices supplémentaires en exécutant la cellule suivante, ou passer à la feuille sur les boucles for. Vous pourrez, par la suite, aborder l’exercice plus avancé ci-dessous.
Exercices supplémentaires#
import glob, os
from jupylates import Exerciser
os.chdir("../exercices")
Exerciser(glob.glob("boucles-while/*"), mode="train")
Exercice 6 ♣#
Complétez le code de la fonction suivante pour qu’elle détermine si \(n\) est un carré parfait. Dans ce cas, elle doit renvoyer
true; sinon elle doit renvoyerfalse.Rappel: un carré parfait est un nombre qui peut s’écrire \(k\times k\) avec \(k\) un entier. Par exemple \(16\) est un carré parfait car \(16 = 4 \times 4\), mais \(20\) n’est pas un carré parfait.
def carreParfait(n):
### BEGIN SOLUTION
k = 0
while ( k * k < n ):
k = k + 1
return k*k == n
### END SOLUTION
Essayez votre fonction sur les exemples suivants :
carreParfait(16) # doit renvoyer true
True
carreParfait(20) # doit renvoyer false
False
Vérifiez que votre fonction passe les tests automatiques suivants :
assert( carreParfait(0) )
assert( carreParfait(1) )
assert( not carreParfait(2) )
assert( not carreParfait(50) )
assert( carreParfait(100) )
assert( not carreParfait(250) )