2.0 Les outils

2.1 Compteurs et accumulateurs

Un compteur, comme son nom l’indique, sert à compter. Par exemple, on peut compter le nombre d’essais dans un jeu. De manière générale, c’est une variable initialisée à 0 qui est incrémentée d’une unité à chaque passage dans une boucle, éventuellement suite à un test.

Nous allons voir quelques exemples.

2.1.1 Compteur et boucle conditionnelle

Sans test
1 def taille(n) :
2  cpt=0
3  while n > 0:
4    cpt = cpt + 1
5    n = n // 2
6  return cpt

On compte le nombre de divisions euclidiennes successives de n par 2, jusqu’à arriver à un quotient nul. On obtient donc le nombre de chiffres dans l’écriture binaire de n.

Avec test
1def nombre_de_1(n) :
2cpt = 0
3while n > 0 :
4  if n % 2 == 1
5    cpt = cpt + 1
6  n = n // 2
7return cpt

Le programme est identique un précédent, mais on incrémente le compteur seulement quand un reste vaut 1. On compte donc le nombre de 1 dans l’écriture binaire de n.

2.2 Compteur et boucle inconditionnelle

Dans une boucle inconditionnelle sans test, un compteur compterait le nombre de passages dans la boucle. Or, ce nombre est connu à l’avance et donc un compteur n’apporterait rien.

2.2.1 Permutation de valeurs

On est souvent amené à devoir permuter des valeurs entre des variables, en particulier échanger les valeurs de deux variables. Cet échange est présent dans les algorithmes de tri exposés au chapitre suivant qui reposent sur la comparaison de deux valeurs et en fonction du résultat leur éventuelle permutation. Dans la construction d’une suite de nombres définie par une relation du type \(u_n+2 = f(u_n+1, u_n)\) , on calcule un terme en fonction des deux précédents. Ceci est possible en utilisant seulement deux variables puisque cela revient à passer du couple \((u_n+1, u_n)\) au couple \((u_n+2, u_n+1)\). Le principe général est simple. Avant tout, que penser du code suivant ?

À la troisième affectation, \(var1\) prend la valeur courante de \(var2\) donc 23. La quatrième affectation utilise la valeur courante de \(var1\), qui est 23 à ce moment, donc \(var2\) prend la valeur 23. Les deux variables ont finalement pour valeur 23, soit la valeur initiale de \(var2\). On comprend qu’il faut modifier la valeur d’une variable sans perdre sa valeur initiale qu’il faut donc stocker dans une troisième variable.

La valeur de \(var1\), 17 est gardée dans temp. Ce nom est choisi car cette variable est temporaire, elle n’est utilisée que le temps de rechange. On peut alors modifier la valeur de \(var1\) en lui affectant la valeur de \(var2\) et finalement affecter à \(var2\) la valeur initiale de \(var1\) qui n’a pas été perdue.

Ce procédé est utilisé dans de nombreux langages. Certains langages possèdent une fonction swap pour permuter deux valeurs. En Python, c’est l’existence du type tuple qui nous permet d’avoir une instruction simplifiée : var1, var2 = var2, var1. Cette instruction signifie que le couple (var1, var2) prend la valeur du couple (var2, var1) c’est-à-dire la valeur (23, 17). Donc var1 prend la valeur 23 et var2 prend la valeur 17 L’échange des valeurs est réalisé.

2.2.2 Tests et boucles

Dans la plupart de nos programmes, nous trouvons des tests qui utilisent les structures if .. - , ou if ... else ..., ou if ... elif .,``. else ….`` Première remarque : else n’est jamais suivi d’une expression. Une expression après else signifierait « sinon, si l’expression a la valeur True » et nous sommes alors dans le cadre d’une structure elif ----"Sinon" signifie : si ce qui est avant est faux ».

Par exemple : Nous avons trois cas distincts : le premier cas est x > 0, le deuxième cas est x < 0, le troisième cas regroupe tout ce qui n’est ni dans le premier cas ni dans le deuxième, donc si \(x\) est nul. L’écriture else x  == 0 provoquerait une erreur.

Par exemple : Si la valeur initiale de \(x\) est 5, alors elle est actualisée à 2. Si la valeur initiale de \(x\) est -2, elle est actualisée à 3. Si la valeur initiale de \(x\) est 0, elle est actualisée à 2.

Une deuxième remarque : la structure if ... if ... else ... n’est pas équivalente à la structure if ... elif .. . else .... En parlant, nous pouvons énoncer : si \(x\) est strictement positif, puis si \(x\) est strictement négatif, sinon, … Et certains comprendront peut être que sinon correspond à \(x\) nul. Ce n’est évidemment pas le cas.

2.2.3 Examinons le code qui suit :

Quelle est la valeur finale de x si la valeur initiale est 5 ?
1if x > 0 :
2  x = x -3
3if x < 0 :
4  x = x + 5
5else :
6  x = x + 2

Nous avons ici deux blocs distincts : le bloc if x > 0 ... ``et le bloc ``if x > 0 ... else .... Donc puisque la valeur de x est strictement positive, elle est actualisée à 2. Ensuite, puisque la valeur courante de x est strictement positive, elle est actualisée à 4. Si la valeur initiale de x est 2, elle est strictement positive donc elle est actualisée à -1. Puis elle est strictement négative donc elle est actualisée à 4.

Troisième remarque : l’expression qui suit if a une valeur True ou False ou une valeur qui peut être interprétée comme True ou False.

Prenons par exemple if x > 0. Dans cet exemple, nous n’écrirons pas if (x > 0) == True. Donc il en est de même si l’expression est composée d’une seule variable booléenne ou d’un appel de fonction renvoyant un booléen. Nous écrirons if b et if f (x) et non pas if b = True ou if f (x) True. Ces deux dernières écritures reviendraient à tester True == True ou False == True.

2.2.4 Boucles

Nos programmes sont constitués de boucles et même de boucles imbriquées. Nous pouvons avoir une ou plusieurs boucles while ou for a l’intérieur d’une boucle while ou for. Par exemple :

1for i in range(4) :
2  for j in range(3) :
3    print(i + j)

Les valeurs successives des variables i et j sont : i = 0 et j = 0, puis j = 1, puis j = 2, ensuite i = 1 et j = 0, puis j = 1, puis j = 2, ensuite i = 3 et j = 0, puis j = 1, puis j = 2.

Pour chacune des quatre valeurs de i, (0, 1, 2, 3), j prend trois valeurs, (0,1, 2), et nous aurons donc douze affichages avec la fonction print.

0
1
2
1
2
3
2
3
4
3
4
5