Les boucles : de 1 à 10

boucle1.cpp


Bon, alors nous avons vu jusqu'ici les notions de base, les variables, les calculs, ainsi que les structures de sélection. Mais il manque encore quelques points importants afin de faire un programme digne de ce nom. En effet, les programmes ont parfois besoin de répéter une ou plusieurs instructions pour effectuer une certaine tâche. C'est même encore plus fréquent que vous ne pourriez l'imaginer. C'est donc à ce problème que nous allons nous mettre aujourd'hui, en voyant un premier type de boucle. Sachez qu'il en existe trois types, et les deux autres (très semblables) seront décrites la semaine prochaine.

Voici le problème

En programmation, on veut souvent pouvoir faire quelque chose un certain nombre de fois. Ce genre de boucle apparaît dans toutes les applications possibles et imaginables : les graphismes, les calculs mathématiques, la cryptologie, l'accès aux fichiers... la liste est trop longue.

L'idée, c'est d'avoir une variable qui va compter le nombre de fois qu'on a exécuté une instruction, afin de savoir quand il faut s'arrêter. Nous partons donc du principe que nous savons à l'avance combien d'itérations nous allons faire. Pour un premier exemple, nous ferons juste une simple boucle qui affiche tous les chiffres de 0 à 9. Regardez le code suivant :

int i;

cout << "Début de la boucle:\n";

for(i = 0; i <= 9; i++)

    cout << i << " ";

cout << "\nFin de la boucle!\n";

D'abord, on crée une variable entière i. C'est i qui va nous permettre de savoir où on en est. Ensuite, vous voyez l'instruction for, avec des parenthèses. L'instruction for est celle qui constitue la boucle, dans laquelle on affiche la valeur de i à chaque itération.

Regardons de plus près ce que contiennent ces parenthèses. Il y a trois expressions, séparées par un point-virgule.

Jettons un petit coup d'oeil à l'affichage que produit l'exemple précédent :

Début de la boucle:
0 1 2 3 4 5 6 7 8 9
Fin de la boucle!

C'est vrai, c'était facile à deviner. Mais ceci montre bien que la valeur de i existe dans la boucle, et qu'on peut donc s'en servir, soit pour un affichage comme ci-dessus, soit pour un calcul (n.b. les racines carrées de nombres sont calculées à l'aide d'une boucle, avec une formule qui permet de s'approcher de la valeur de la racine carrée en la recalculant un certain nombre de fois). On voit aussi que toutes les valeurs sont prises, y compris la valeur initiale.

Pour mettre les choses au clair, voici la syntaxe générale d'une boucle for:

for(initialisation; condition de continuation; incrémentation)

    instruction; ou { bloc d'instructions; }

Les subtilités

La boucle for du C/C++ est une boucle très puissante, et souvent plus flexible que les boucles équivalentes d'autres langages (le Basic et le Pascal ont aussi des boucles for, mais leur syntaxe est un peu différente).

Il existe un certain nombre d'asctuces à savoir si on veut bien profiter des boucles. Nous allons les voir ici, sans pour autant forcer la dose de connaissances à ingurgiter pour la première fois que vous voyez les boucles.

Les boucles emboîtées

Rien ne vous empêche d'inclure dans les instructions d'une boucle une autre boucle. Voici un petit exemple, et son affichage :

int i, j;

cout << "Début de la boucle:\n";

for(i = 1; i <= 4; i++)

    for(j = 1; j <= i; j++)

      cout << "i = " << i << " et j = " << j << endl;

cout << "Fin de la boucle!\n";

Voici l'affichage produit par cet exemple :

Début de la boucle:
i = 1 et j = 1
i = 2 et j = 1
i = 2 et j = 2
i = 3 et j = 1
i = 3 et j = 2
i = 3 et j = 3
i = 4 et j = 1
i = 4 et j = 2
i = 4 et j = 3
i = 4 et j = 4
Fin de la boucle!

On constate que pour chaque valeur de i, j prend toutes les valeurs entre 1 et i. Nous ne nous étendrons pas plus loin pour le moment, mais il faut savoir que les boucles emboîtées sont très utilisées aussi.

Après la boucle

Revenons à une boucle simple, et intéressons-nous à la valeur de i après la boucle :

int i;

cout << "Début de la boucle:\n";

for(i = 0; i <= 9; i++)

    cout << i << " ";

cout << "\nFin de la boucle!\n";
cout << "i vaut : " << i << endl;

Voici l'affichage produit :

Début de la boucle:
0 1 2 3 4 5 6 7 8 9
Fin de la boucle!
i vaut : 10

En effet, la boucle s'arrête si la condition i <= 9 n'est plus vérifiée. Or lors du dernier passage dans la boucle, i vaut 9, et ensuite, l'expression d'incrémentation à lieu, ce qui signifie qu'après le dernier passage, i vaut 10. On revient alors à la condition i <= 9, qui cette fois-ci est fausse : on arrête tout. Voilà pourquoi, à la fin de la boucle, i vaut 10 (en réalité, i contient une valeur qui ne répond plus à la condition de continuation).

L'initialisation du compteur

L'une des raisons pour lesquelles la boucle for est si flexible, c'est parce que chacune des trois expressions dans les parenthèses est facultative : vous n'avez pas besoin de préciser de valeur initiale, ni de condition d'arrêt, ni d'incrémentation. Voici un petit exemple qui utilise deux boucles, l'une après l'autre, en reprenant ce que nous venons de voir juste avant :

int i;

cout << "Début de la boucle:\n";

for(i = 0; i < 5; i++)

    cout << i << " ";

cout << "\nFin de la première boucle!\n";

for(; i <= 9; i++) // Pas d'initialisation!

    cout << i << " ";

cout << "\nFin de la seconde boucle!\n";

Pouvez-vous deviner l'affichage produit? Après la première boucle, 5 n'est pas affiché, mais i vaut 5. Voici ce que ça donne :

Début de la boucle:
0 1 2 3 4
Fin de la première boucle!
5 6 7 8 9
Fin de la seconde boucle!

L'incrémentation

De la même façon, vous pouvez tricher avec l'incrémentation :

int i;

cout << "Début de la boucle:\n";

for(i = 1; i <= 10; ) // Pas d'incrémentation!
{

    cout << i << " ";
    i *= 2;

}
cout << "\nFin de la boucle!\n";
cout << "i vaut : " << i << endl;

Voici l'affichage :

Début de la boucle:
1 2 4 8
Fin de la boucle!
i vaut : 16

Avec la même boucle, mais en laissant l'expression d'incrémentation i++, on aurait obtenu :

Début de la boucle:
1 3 7
Fin de la boucle!
i vaut : 15

car même si vous modifiez la valeur du compteur dans la boucle, l'incrémentation est quand même appliquée entre la fin des instructions et le passage suivant.

Sortir de la boucle

Il peut être préférable, dans certains cas, de sortir de la boucle avant que la condition d'arrêt n'ait eu lieu. Si vous voulez sortir d'une boucle dans laquelle vous êtes directement (c'est-à-dire que vous n'êtes pas dans une sous-boucle), vous avez à votre disposition l'instruction break, comme pour le switch. Cette instruction sort directement de la boucle dans laquelle vous êtes. Son utilisation est illustrée dans le programme du jour, affiché à la fin du cours.

Sachez cependant que l'instruction break ne sort que de la boucle dans laquelle vous vous trouvez directement. Il vous faudra avoir recours à d'autres astuces pour sortir de toutes les boucles emboîtées d'un coup. Mais je pense que le jour où vous aurez besoin de faire cela, vous trouverez vous-même la solution. Je ne vais donc pas en rajouter pour le moment.

Il existe également une autre instruction pour contrôler une boucle for : continue. Cette instruction saute directement après la dernière instruction de la boucle, mais sans en sortir. On passe donc directement à l'itération suivante.

Le programme du jour

Voici un cours consistant : on a introduit une boucle, on a vu quelques-unes des subtilités qu'elle présente... ils serait temps d'en faire quelque chose. Aujourd'hui, le programme du jour se présente sous la forme d'un jeu : l'utilisateur à 10 essais pour deviner un nombre compris entre 0 et 99. Pour le moment, nous ne savons pas vraiment comment générer des nombres aléatoires, alors le nombre sera toujours le même à chaque fois. Nous utilisons des constantes, une boucle, une instruction break et aussi quelques ifs :

1
2
3

4
5
6
7

8
9

10
11

12
13
14
15
16

17

18

19

20

21

22

#include <iostream.h>
main()
{

    const int MAX = 10;
    const int NOMBRE = 61;
    int essai = 1;
    int proposition = 0;

    cout << "Le nombre caché!!!\n";
    cout << "Vous avez " << MAX << " essais pour trouver le nombre caché.\n";

    for(essai = 1; essai <= MAX; essai++)
    {

      cout << "Essai n° " << essai << ". Votre proposition : ";
      cin >> proposition;
      if(proposition < NOMBRE) cout << "Plus!!!\n";
      if(proposition > NOMBRE) cout << "Moins!!!\n";
      if(proposition == NOMBRE) break;

    }

    if(essai > MAX)

      cout << "Perdu!\n";

    else

      cout << "Gagné!\n";

}

Le fonctionnement du programme est simple et ne nécessite pas trop d'explications. La constante MAX illustre la facilité de changer de nombre d'essais, sans toucher à rien d'autre dans le programme (voyez le cours de la semaine dernière, Les structures de sélection : un peu plus loin).

Voici les points à retenir :

  • La boucle for se caractérise par un compteur, une initialisation de ce compteur, une condition de continuation et une expression d'incrémentation.
  • Chacune des trois dernières expressions est facultative.
  • D'abord, le compteur prend sa valeur initiale. Ensuite, avant chaque passage, la condition de continuation est vérifiée, et après chaque passage, l'expression d'incrémentation est calculée.
  • La valeur du compteur est visible dans la boucle.

Et puis les exercices, parce que sans exercices, vous ne serez jamais bon programmeur (car c'est en forgeant qu'on devient programmeur, vous devriez le savoir.)

  • Ecrivez un petit programme qui affiche les valeurs de 9 à 0. Utilisez pour cela l'expression de décrémentation i--.
  • Ecrivez, dans un autre petit programme, une boucle qui affiche tous les nombres pairs de 0 à 20, à l'exception des mutliples de 6 (Conseil : utilisez un if dans la boucle).
  • Reprenez le premier programme du cours, mais changez l'incrémentation de i++ en ++i. Que se passe-t-il ?
  • Ecrivez un petit programme avec une boucle affichant un petit message, mais sans mettre aucune expression entre les parenthèses (c'est à dire : for( ; ; )). Que se passe-t-il lorsque vous exécutez le programme? (Si votre programme se comporte bizarement, appuyez sur CTRL+C).
  • Dans le programme du jour, pourriez-vous utiliser une boucle pour vous assurer que l'utilisateur rentre bien une valeur comprise entre 0 et 99, et en le faisant recommencer la saisie à chaque fois que ce n'est pas respecté. (Conseil : vous n'avez pas besoin de compteur ni d'incrémentation pour cette boucle).
  • De même, pourriez vous faire une boucle qui permette à l'utilisateur de rejouer s'il a perdu (cette boucle devra englober toutes les autres boucles du programme). Bien sûr, il faudra lui demander (avec une saisie) s'il veut rejouer ou non.
  • A partir de là, pourriez-vous trouver un mécanisme pour arrêter le programme si l'utilisateur entre -1 comme proposition ?

Revenons un instant sur l'exercice n° 5 : nous utilisons une boucle pour nous assurer que l'utilisateur a bien saisi une valeur comprise entre 0 et 99. C'est en effet commun de devoir contrôler une saisie, histoire qu'il ne se présente pas trop d'imprévus dans un programme. Mais si vous avez essayer de faire cette saisie contrôlée, vous vous êtes sans doute rendu compte que la boucle for n'est pas vraiment la boucle idéale pour faire cela.

Avec les boucles de la semaine prochaine, nous verrons qu'il y a de meilleures solutions à ce petit problème.


Voir aussi: les opérateurs - Les structures de sélection : un peu plus loin