Boucler sur une condition (while)

Les boucles for permettent de parcourir les éléments d’un itérable, mais un autre type de boucle est possible : les boucles testant une condition.

À la manière d’un if qui exécute un bloc si une condition est vraie, il s’agira ici d’exécuter un bloc tant que cette condition est vraie.

Boucler sur une condition

C’est ainsi qu’entre en scène la boucle while (qui signifie littéralement « tant que ») et qui sert à boucler sur un prédicat. Un bloc while est alors assez similaire à un if : on a le mot-clé while suivi d’une expression conditionnelle et d’un :, puis les lignes indentées qui correspondent au contenu du bloc.

Ce contenu sera exécuté en boucle tant que le prédicat est vrai, celui-ci étant testé à nouveau avant chaque itération. Il convient donc dans le contenu du bloc de faire varier les valeurs utilisées par le prédicat, afin qu’il finisse par être faux et que l’on sorte de la boucle.

answer = 'o'

while answer == 'o':
    print('Vous êtes dans la boucle')
    answer = input('Souhaitez-vous rester dans la boucle (o/n) ? ')

print('Vous êtes sorti de la boucle')
Vous êtes dans la boucle
Souhaitez-vous rester dans la boucle (o/n) ? o
Vous êtes dans la boucle
Souhaitez-vous rester dans la boucle (o/n) ? o
Vous êtes dans la boucle
Souhaitez-vous rester dans la boucle (o/n) ? n
Vous êtes sorti de la boucle

À la première ligne de mon fichier, j’initialise une variable answer à 'o'. Sans elle, la condition de mon while ne serait pas valide puisque answer n’aurait pas encore été définie (elle n’est définie ensuite que dans le bloc du while).

Je lui donne comme valeur 'o' pour que la condition du while soit vraie et que l’on puisse entrer dans le bloc. Si j’initialise la variable à 'n', alors la condition sera fausse dès le premier test et le contenu du bloc jamais exécuté.

Pour chaque tour de boucle, l’expression conditionnelle est à nouveau évaluée. Si elle s’évalue à faux, la boucle s’arrête immédiatement. Ainsi dans mon exemple, à la 4ème itération de la boucle, answer vaut maintenant 'n' (c’est la valeur qui lui a été donnée dans la 3ème itération). L’expression étant fausse, la boucle se termine et son contenu n’est pas exécuté pour la 4ème itération.

Mais l’expression conditionnelle n’est testée qu’au tout début de chaque itération, pas au milieu de celle-ci, ce qui fait que la boucle ne peut se terminer qu’à un moment bien précis. Regardons par exemple le code qui suit :

pv = 50

print('Pythachu a', pv, 'PV')

while pv > 0:
    print('Pythachu perd 20 PV')
    pv -= 20
    print('Il lui reste maintenant', pv, 'PV')

print('Pythachu est KO, avec', pv, 'PV')
Pythachu a 50 PV
Pythachu perd 20 PV
Il lui reste maintenant 30 PV
Pythachu perd 20 PV
Il lui reste maintenant 10 PV
Pythachu perd 20 PV
Il lui reste maintenant -10 PV
Pythachu est KO, avec -10 PV

On constate bien qu’au cours de la 3ème itération, le nombre de PV devient inférieur à 0. Mais l’itération continue (on affiche le message), ce n’est qu’à l’itération suivante que l’expression est recalculée et que la boucle se termine.

On remarque aussi que la valeur pv existe toujours après la boucle, et possède la dernière valeur qui lui a été assignée.

Vers l'infini et au-delà

Notre boucle while s’arrête quand le prédicat devient faux. Mais que se passe-t-il alors si celui-ci est toujours vrai ?
Notre boucle se retrouve alors à tourner indéfiniment…

>>> while True:
...     print("Vers l'infini et au-delà !")
... 
Vers l'infini et au-delà !
Vers l'infini et au-delà !
Vers l'infini et au-delà !
Vers l'infini et au-delà !
Vers l'infini et au-delà !
Vers l'infini et au-delà !
Vers l'infini et au-delà !
[...]

Quand votre programme rencontre une boucle infinie, utilisez la combinaison de touches Ctrl+C pour l’interrompre.

Bien sûr, nous avons ici écrit volontairement une boucle infinie, mais celles-ci sont plus insidieuses et peuvent parfois se cacher là où on ne les attend pas. Prenons par exemple le programme suivant qui a pour but de calculer la factorielle1 d’un nombre.

n = int(input('Entrez un nombre : '))

i = n
fact = 1
while i != 0:
    fact *= i
    i -= 1

print('La factorielle de', n, 'vaut', fact)
factorielle.py

Ce code fonctionne très bien pour des entiers naturels :

% python factorielle.py
Entrez un nombre : 5
La factorielle de 5 vaut 120
% python factorielle.py
Entrez un nombre : 1
La factorielle de 1 vaut 1

Mais dans le cas où l’on entre un nombre négatif, le programme se met à boucler indéfiniment et l’on doit le couper avec un Ctrl+C.

% python factorielle.py
Entrez un nombre : -1
^CTraceback (most recent call last):
  File "factorielle.py", line 6, in <module>
    fact *= i
KeyboardInterrupt

En effet, pour un nombre négatif la condition n != 0 sera toujours vrai puisque le nombre est décrémenté à chaque tour de boucle (il restera négatif et ne sera jamais nul). Dans l’idéal il faudrait donc traiter les nombres négatifs comme une erreur et afficher un avertissement dans ces cas-là pour prévenir toute boucle infinie.

Le souci est qu’à l’exécution il n’est théoriquement pas possible de savoir si une boucle va s’arrêter ou non, c’est un problème indécidable (problème de l’arrêt) : dans le cas précédent on ne sait pas quelle valeur sera donnée à notre programme puisqu’elle dépend d’une saisie de l’utilisateur.
Ainsi, il faut être prudent et faire très attention aux conditions utilisées pour les while et bien s’assurer que celles-ci finissent toujours par devenir fausses.

Mais nous découvrirons par la suite qu’il y a des usages légitimes de boucles infinies, et des moyens de contrôler le déroulement de la boucle.


  1. La factorielle est la fonction mathématique calculant le produit des nombres entiers de 1 à n. Ainsi la factorielle de 5 est 1×2×3×4×51 \times 2 \times 3 \times 4 \times 5 soit 120.

Boucle for ou boucle while ?

Avec nos deux types de boucles, on pourrait se demander quand utiliser l’une et quand utiliser l’autre.

Un bloc while correspond à l’action de répéter. On répète un même traitement et on fait varier les paramètres.
Il peut s’agir d’attendre une certaine entrée utilisateur ou d’affiner un calcul par exemple. Le but est alors que la boucle while serve à construire/calculer une valeur que l’on réutilisera par la suite.

On peut par exemple imaginer une boucle while pour calculer itérativement la racine carrée de 2 selon la méthode de Héron, en s’arrêtant quand une certaine précision a été atteinte.

x = 1

while abs(2 - x**2) > 0.001:
    x = (x + 2/x) / 2

print('La racinne carrée de 2 vaut environ', x)

Pour tout le reste, il y a la boucle for.

Un bloc for correspond à l’action d’itérer, de parcourir des éléments.
Quand on souhaite exécuter une action pour chaque valeur d’une séquence identifiable (éléments d’une liste, caractères d’une chaîne, nombres d’un intervalle, etc.) c’est un for qui doit être utilisé, pour tout ce qui peut s’apparenter à de l’itération sur des valeurs.

Dans l’exemple des boucles infinies, nous n’aurions par exemple pas rencontré de problème en utilisant une boucle for. Ces dernières sont en effet plus facilement prédictibles si l’on sait que l’on va itérer sur un ensemble fini d’éléments (tel qu’un intervalle de nombres).

n = int(input('Entrez un nombre : '))

fact = 1
for i in range(2, n + 1):
    fact *= i

print('La factorielle de', n, 'vaut', fact)