tite fractale

Variables mutables

1. Introduction

Un ordinateur ne se souvient pas naturellement d’un travail qu’il a effectué. Il faut explicitement lui donner l’ordre de le faire. Suivant la quantité de données, la structure ou la durée du « souvenir » que l’on veut créer, on utilise différents mécanismes :

C’est cette dernière méthode que nous allons étudier ici. Les variables mutables (qui peuvent être modifiées) sont stockées le plus souvent dans la mémoire vive.

2. Déclaration

2.1. Rôle

Tous les langages ne nécessitent pas de déclaration (Python n’en a pas besoin par exemple), mais on le fait quand même souvent en algorithmique. Cela rend les algorithmes plus lisibles et prépare le terrain pour les langages qui en ont besoin.

Les déclarations réservent un espace dans la mémoire vive pour que le programmeur puisse y stocker une valeur. Il faut bien sûr que la taille de cet espace soit adapté à la taille de l’information à stocker.

2.2. Plusieurs notations

3. Affectation

3.1. Rôle

L’affectation consiste à mettre la valeur d’une expression dans une case mémoire repérée par le nom de la variable (penser à une boîte).

3.2. Plusieurs notations

<- se lit « prend la valeur » ou « prend pour valeur ».

3.3. Exemples

>>> ma_var = 1             # simple affectation
>>> ma_var
1
>>> mon_autre_var = 2 + 2  # prend la valeur de l’expression
>>> mon_autre_var
4
>>> mon_autre_var = ma_var # prend la valeur contenue dans ma_var
>>> mon_autre_var
1
>>> ma_var = ma_var + 1    # incrémentation
>>> ma_var
2
>>> mon_autre_var          # n’a pas subi l’incrémentation
1

3.4. Remarques

3.4.1. Erreurs fréquentes

Il faut que la partie gauche de l’affectation soit bien un nom de variable : attention en Python à ne pas mettre de tiret - dans les noms de variable.

Le signe - est compris comme une soustraction.

Seul le contenu de la variable dont le nom est à gauche est changé, et ceci seulement au moment de l’affectation. L’affectation n’attache pas à la variable le comportement à venir de l’expression (même si c’est parfois le cas dans certains langages quand la valeur est « passée par référence »).

3.4.2. Incohérences avec les mathématiques

L’expression est souvent calculée entièrement avant que le résultat ne prenne place dans la case correspondant au nom de la variable, ce qui amène à des incohérences avec le monde des mathématiques.

Le signe = de certains langages a un sens totalement différent de celui qu’il a en mathématiques.

texte à comparer Maths Info
x = 1 égalité, proposition comparant x et 1 en général, c’est une affectation
x == 1 ne veut rien dire expression qui compare x et 1, retourne souvent un booléen
x = x + 1 proposition toujours fausse la valeur de la variable x est augmentée de 1 (incrémentation)

C’est le moment d’expérimenter avec la console intéractive de Python :

>>> 0 = 0
  File "<stdin>", line 1
SyntaxError: can't assign to literal
>>> 0 == 0
True
>>> 1 == 0
False
>>> x == 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> x = 0
>>> x == 0
True
>>> x == 1
False
>>> x = 0 == 0
>>> x
True

En anticipant sur la notion de type :

>>> type(0)
<class 'int'>
>>> type(True)
<class 'bool'>

3.4.3. Inconvénients des variables mutables

Il n’y a pas « de transparence référentielle » : un x à un endroit d’un programme n’a peut-être pas la même valeur deux lignes plus loin, alors qu’en maths, $x$ représente toujours le même nombre. Cela devient un vrai jeu de piste pour savoir quelle est la valeur d’une variable à un moment donné.

L’ordre des affectations est important. Exécuter x <- 0 puis x <- 1 n’aura pas le même effet que x <- 1 puis x <- 0.

Entraînez-vous à faire les exercices de tête !!!

4. Types

Les opérateurs travaillent souvent sur des valeurs de même type. Par exemple, 4 / 2 signifie quelque chose, alors que Faux / Vrai ne signifie rien. Voici une liste non exhaustive des types que l’on peut rencontrer en informatique…

4.1. Booléens

4.1.1. Valeurs

Faux Vrai (Français, Algo)
0 1 (Électr…)
False True (Python)

4.1.2. Opérations

Opérations Algo Python
négation non not
disjonction et and
conjonction ou or

4.1.3. Exemples

>>> True and False
False
>>> (True or False) and (False or True)
True

4.1.4. Remarques

4.1.4.1. Place prise en mémoire

Même si un booléen ne pourrait être codé que sur un seul bit, il est quasiment toujours codé sur un octet (gâchis !).

4.1.4.2. Polymorphisme en Python

En Python, nous verrons que n’importe quelle valeur peut servir (passer pour) un booléen. Par exemple, parmi les nombres, 0 est faux et tous les autres nombres sont vrais.

4.2. Entiers

4.2.1. Valeurs

La plage des entiers utilisables dépend de beaucoup de choses. En particulier de :

Avec des mots de 32 bits, on peut coder 4 294 967 296 valeurs différentes, donc les entiers de 0 à 4 294 967 295, ou les entiers de -2 147 483 648 à 2 147 483 647 (voir le cours sur le binaire).

Cet article Wikipedia détaille ce genre de considérations avec différentes tailles de mots.

On peut dépasser la contrainte de la longueur des mots binaires avec des algorithmes. On déplace les calculs du matériel vers le logiciel (voir ce pdf de l’INRIA) pour pouvoir traiter des nombres entiers arbitrairement grands (précision arbitraire), dits bignums. Python sait les manipuler et ajoute le suffixe L quand il passe à ce type de nombres :

>>> 2**3      # 2³
8
>>> 2**30
1073741824
>>> 2**31
2147483648L   # Testé avec Python 2 sur un ordinateur 32 bits.
>>> 2**70     # 70 > 64 !!!
1180591620717411303424L

4.2.2. Opérations

Opérations Algo Python
addition + +
soustraction - -
multiplication × *
division (*) div / ou // (**)
reste mod %
puissance 2**3

(*) euclidienne ou entière

(**) Python 2 ou 3

4.2.2.1. Exemples avec la division

>>> 147 // 2
73
>>> 147 % 2
1

4.2.2.2. Remarque

Le reste de la division euclidienne par 2 permet donc de tester la parité d’un nombre. Que teste-t-on ici et que va répondre Python ?

>>> 147 % 2 == 0
On teste la parité de 147. Python répondra : False, ce qui confirme que 147 est impair.

4.2.2.3. Incrémentation et autres

Incrémenter signifie « augmenter d’une valeur entière fixée », en général de 1. On peut par exemple incrémenter n grâce à l’instruction n <- n + 1 en algorithmique, ou grâce à l’instruction Python n = n + 1, que l’on peut abréger en n += 1 (et non avec n++ qui n’existe pas en Python).

Voici d’autres exemples de ce genre d’abbréviations :

>>> n = 0
>>> n += 1
>>> n
1
>>> n *= 2
>>> n
2
>>> n //= 2
>>> n
1
>>> n -= 1
>>> n
0

4.3. Autres nombres

Les nombres entiers ne suffisent parfois pas pour représenter certaines situations. Avec les Objets vus plus loin, on peut représenter beaucoup de choses, dont des nombres (rationnels, imaginaires…).

Mais sans aller chercher trop loin dans le Grand Zoo des Mathématiques, nous avons besoin que les ordinateurs manipulent des nombres pour décrire des montants d’argent, des valeurs de température, ou des distances, petites et grandes, et bien sûr non entières.

Plusieurs types ont été inventés, dont les nombres à virgule fixe, qui ne permettent cependant pas de travailler avec des nombres très grands et des nombres très petits. Un type est très utilisé pour approximer les nombres réels : les nombres à virgule flottante, que nous appellerons ici flottants.

4.4. Flottants

4.4.1. Valeurs

Les nombres flottants servent à représenter les nombres réels. Ils ne le font qu’approximativement, mais ce n’est pas essentiel à comprendre au début.

On peut démontrer qu’il y a autant de nombres entiers positifs que de nombres relatifs, ou que de nombres décimaux ou même que de nombres rationnels. Les nombres réels ont ceci de spécial, c’est qu’il y en a « strictement plus » que de nombres entiers. voir cet article sur Wikipedia pour une démonstration. Comme un ordinateur ne peut pas manipuler le « continu » mais uniquement le « dicret », il ne peut manipuler correctement les nombres réels.

D’autre part, les flottants introduisent des erreurs de calcul, et ceci sans qu’il n’y ait aucun bug. C’est leur destin.

Les flottants sont un domaine d’études assez vaste, qui dépasse le cadre de cette présentation. Il faut retenir que les flottants possèdent :

Le flottant est le produit $s.m.b^e$ où b est la base de représentation (souvent 2). Lorsque $e$ varie, la virgule « flotte ».

Dans beaucoup de langages, on se sert d’un point pour la virgule (c’est ce que font les anglophones). Pour qu’un nombre entier soit vu comme flottant par la machine, on se sert aussi du point, éventuellement suivi d’un zéro (voir plus loin les opérations sur les flottant).

4.4.2. Normes

Suivant la taille des mots binaires de la machine et les choix de place donnée à la mantisse ou l’exposant, les mêmes mots binaires ont des significations différentes et l’ensemble des différents flottants que l’on peut atteindre varie. Il faut donc convenir du rôle de chaque bit. C’est l’objectif de certaines normes.

Pour plus de détails, consulter ces articles sur Wikipedia : Nombre flottant et Norme IEEE_754.

4.4.3. Opérations

4.4.3.1. Les quatres opérations habituelles

Nous retrouvons l’addition, la soustraction, la multiplication et la division. En Python, attention au comportement de cette dernière, suivant la version du langage, le symbôle utilisé (/ ou //) et le type des opérandes.

>>> 2.5 + 2.5
5.0
>>> 2.0 - 2.
0.0
>>> 5//2  # Python 2
2
>>> 5/2
2
>>> 5.//2.
2.0
>>> 5./2.
2.5
>>> 5//2  # Python 3
2
>>> 5/2
2.5
>>> 5.//2.
2.0
>>> 5./2.
2.5

Python permet aussi de faire des opérations avec des opérandes mixtes :

>>> 5./2
2.5
>>> 5/2.
2.5
>>> 2 + 2.
4.0

4.4.3.2. Attention à la précision

Les flottants ne permettent pas des calculs en précision arbitraire.

D’une part leur taille est plafonnée, à la fois vers les grands nombres :

>>> 2**52
4503599627370496
>>> 2.**52 + 1
4503599627370497.0
>>> 2**53
9007199254740992
>>> 2.**53 + 1
9007199254740992.0

et vers les petits :

>>> 10**-322/10
1e-323
>>> 10**-323/10
0.0

D’autre part leur granularité est, disons, « subtile » :

>>> 0.1*2
0.2
>>> 0.3
0.3
>>> 0.1*3
0.30000000000000004

Ils restent assez précis pour travailler par exemple avec des valeurs physiques, puisque par exemple la constante de gravité n’est connue à 6 décimales près. En revanche, certains systèmes financiers préfèrent éviter de les utiliser.

4.4.3.3. De nouvelles opérations

De « nouvelles » opérations sont possibles (parfois uniquement grâce au type du résultat).

>>> 5/2              # Uniquement en Python 3.
2.5
>>> int(2.5)         # D’autres fonctions existent pour la partie entière.
2                    # Ici le résultat est de type « entier ».
>>> import math      # Certaines fonctions sont dans des modules.
>>> math.floor(2.5)  # Python 2
2.0
>>> math.floor(2.5)  # Python 3
2
>>> math.sqrt(2)     # racine carrée
1.4142135623730951
>>> math.exp(1)
2.718281828459045    # exponentielle

4.4.3.4. Approfondissement

4.5. Listes

Attention, ce type n’apparaît pas dans tous les langages. Comme il est important en Python, on en parle ici.

Les listes contiennent une suite ordonnée (au sens où l’ordre est important) de valeurs.

>>> [1, 2] == [1, 2]
True
>>> [1, 2] == [2, 1]
False

Suivant les langages, les listes peuvent ne pas contenir des valeurs de même type : [True, 1, 1.5].

4.5.1. Opérations internes

Avec une seule liste :

>>> len([False, True, False])
3
>>> sorted([3, 1, 2])
[1, 2, 3]
>>> sorted([3, 1, 2], reverse=True)
[3, 2, 1]

Avec deux listes :

>>> [1, 2] + [3, 4]  # Concaténation (réutilisation du signe +).
[1, 2, 3, 4]
>>> [1, 2] + []      # [] est la liste vide et est l’élément neutre
[1, 2]               # de la concaténation.
>>> zip([1, 2], [True, False])  # Moins important, mais c’est un autre exemple.
[(1, True), (2, False)]         # « zip » signifie « fermeture éclair »

4.5.2. Opérations externes

On peut aussi imaginer des opérations qui mettent en jeu une liste et un nombre. Imaginez ce que l’on peut par exemple faire avec [1, 2] et 3 (ou un autre nombre que 3).

>>> [1, 2] ? 3
[1, 1, 1, 2, 2, 2]
>>> [1, 2] ? 3           # 1
[1, 2, 3]
>>> [1, 2] ? 1           # 2
2
>>> [1, 2] ? 3           # 3
[[1, 2], [1, 2], [1, 2]]
>>> [1, 2] ? 3           # 4
[1, 2, 1, 2, 1, 2]
>>> [1, 2] ? 3           # 5
[3, 6]
>>> [1, 2] ? 3           # 6
[4, 5]
...

On retiendra surtout qu’en Python,

1) Ajouter un élément à une liste (idée 1) peut se faire ainsi :

>>> l = [1, 2]
>>> l.append(3)
>>> print(l)
[1, 2, 3]

2) Connaître la valeur à une certaine position dans une liste (idée 2) s’écrit ainsi :

>>> [False, True][1]
True
La première valeur d’une liste est à la position 0.

Cette syntaxe se décline de multiples façons, en utilisant des nombres négatifs pour compter à partir de la fin de la liste, et avec deux nombres pour définir une sous-liste.

>>> l = [1, 2, 3, 4]
>>> l[0]    # contenu à la position 0
1
>>> l[1]    # à la position 1
2
>>> l[-1]   # à la dernière position (-2 -> avant dernière…)
4
>>> l[1:3]  # sous-liste à partir de la position 1 jusqu’à avant la position 3
[2, 3]      # (le « avant » est strict, ce qui est cohérent avec range)
>>> l[1:]   # sous-liste à partir de la position 1
[2, 3, 4]
>>> l[:3]   # sous-liste avant la position 3 (strictement)
[1, 2, 3]
>>> l[:-1]  # sous-liste avant la dernière position (strictement)
[1, 2, 3]
>>> l[-2:]  # sous-liste à partir de l’avant-dernière position
[3, 4]

3) 2*l vaut l + l, 3*l vaut l + l + l… C’est l’idée 4. On remarque que c’est cohérent avec l’addition et la multiplication des nombres.

4) Python ne comprendra pas [1, 2] + 3 (erreur). Même si on aurait pu imaginer qu’il réalise l’idée 6.

À retenir aussi : Dans certains langages, les listes sont appelées tableaux ou vecteurs, ont une taille fixe et ne peuvent parfois contenir que des valeurs de même type.

4.5.3. Génération de listes d’entiers en Python

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(1, 10)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> range(1, 11)
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> range(1, 11, 4)
[1, 5, 9]

4.5.4. Valeur booléenne en Python

La liste vide passe pour le booléen Faux. Toutes les autres listes sont Vraies. On observe que  :

>>> bool([])
False
>>> bool([False])
True

Cela permet surtout de tester si une liste est vide de manière concise avec if liste: plutôt qu’avec if len(liste) == 0.

4.6. Caractères

4.6.1. Valeurs

Leur représentation en binaire a évolué au fil du temps et au fil des normes (ASCII, UTF-8, ISO-8859…) même si une rétro-compatibilité a été assurée. C’est ce que l’on appelle l’encodage des chaînes de caractères (domaine d’étude lui aussi assez vaste).

Python n’a pas de type caractère. C’est simplement une chaîne de caractères de longueur exactement égale à 1, qu’il faut délimiter par des guillemets simples ' ou doubles ".

4.6.2. Opérations

On peut passer d’un caractère à l’entier qui le code et inversement en utilisant respectivement les fonctions ord et chr. Attention, les résultats dépendent bien sûr de l’encodage.

>>> ord('A')
65
>>> chr(97)
'a'
>>> [chr(i) for i in range(32, 127)]  # Syntaxe qui n’est pas à connaître.
[' ', '!', '"', '#', '$', '%', '&', "'",
'(', ')', '*', '+', ',', '-', '.', '/', '0', '1',
'2', '3', '4', '5', '6', '7', '8', '9', ':', ';',
'<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c',
'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
'x', 'y', 'z', '{', '|', '}', '~']

4.6.3. Valeur booléenne en Python

Un caractère est toujours Vrai.

4.7. Chaînes de caractères

4.7.1. Valeurs

C’est le mot technique pour le type qui contient du texte. Les chaînes de caractères sont souvent délimitées par des guillemets simples ' ou doubles " (comme par exemple en Python).

En Python, les chaînes de caractères sont des listes de caractères et héritent à ce titre de toutes les opérations liées aux listes. Elles en ont encore beaucoup d’autres qui leur sont spécifiques (minuscules, majuscules, formater, découper selon les espaces…).

4.7.2. Ne pas confondre

>>> bonjour = "salut"
>>> txt = bonjour
>>> print(txt)
salut
>>> txt = "bonjour"
>>> print(txt)
bonjour
>>> print(salut)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'salut' is not defined

4.7.3. Valeur booléenne en Python

La même que celle des listes puisque les chaînes de caractères sont des listes.

4.8. Dictionnaires

4.8.1. Valeurs

Ils ressemblent à une liste mais les valeurs ne sont pas rangées à des positions repérées seulement par des nombres entiers. Ici par exemple, les valeurs peuvent être désignées par une chaîne de caractères :

>>> d = {"prenom": "Emilie", "nom": "Noether", "age": 36}
>>> d["nom"]
'Noether'

Les dictionnaires sont appelés structures en algorithmie et dans certains langages.

4.8.2. Opérations

En cours de rédaction…

4.8.3. Valeur booléenne en Python

Les dictionnaires vides sont False. Tous les autres sont True.

4.9. Objets

4.9.1. Définition

Certains langages, dits orientés objets permettent au codeur d’inventer et de réaliser de nouveaux types. Les objets possèdent quasiment toujours ces deux propriété (entre autres) :

Pour un avant-goût de toutes les saveurs différentes que l’on peut trouver dans le monde de la programmation orientée objet (POO, OOP en anglais), voir cette page (en anglais).

4.9.2. Valeur booléenne en Python

Au choix du concepteur de l’objet.

5. Variables, expressions et instructions

C’est le moment de faire le point sur les différences entre variables, expressions et instructions.

Tout d’abord, il faut admettre cela dépend des langages. On peut quand même énoncer ces généralités :

Remarque : i++ est à mi-chemin entre expression et instruction.

Voir les articles Wikipedia sur :

6. Tracer des variables

Lorsque l’on veut comprendre comment fonctionne un algorithme, ou pourquoi il ne fonctionne pas correctement, il peut être intéressant de suivre l’état des variables ou de suivre les appels des procédures. On dit qu’on « trace l’algorithme ».

Pour tracer des variables, on utilise souvent un tableau. On peut décider par exemple de noter la valeur des variables à la fin de chaque boucle, voire après l’exécution de chaque ligne.

Exemple :

somme <- 0
n <- 1
Tant que n <= 5
 |  somme <- somme + n
 |  n <- n + 1
Fin Tant que
Afficher somme

Après chaque ligne :

somme n
0 ?
0 1
1 1
1 2
3 2
3 3
6 3
6 4
10 4
10 5
15 5
15 6

Après chaque étape ou à la fin de chaque boucle :

somme n
0 1
1 2
3 3
6 4
10 5
15 6



Christophe Gragnic, le 26/09/2014, 15h05'58".






Page générée le 04/12/2016, 10h08'07" (source).
historique de la page
historique global

 TogetherJS