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.
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.
nom_variable : son_type
pour une variable,nom1, nom2 : son_type
pour plusieurs variables d’un même type,var nom_variable : son_type
,Type nom_variable
,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).
nom_variable <- expression
,nom_variable := expression
,nom_variable = expression
,<-
se lit « prend la valeur » ou « prend pour valeur ».
>>> 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
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.
-
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 »).
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'>
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 !!!
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…
Faux | Vrai | (Français, Algo) |
0 | 1 | (Électr…) |
False | True | (Python) |
Opérations | Algo | Python |
---|---|---|
négation | non | not |
disjonction | et | and |
conjonction | ou | or |
>>> True and False
False
>>> (True or False) and (False or True)
True
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 !).
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.
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
Opérations | Algo | Python |
---|---|---|
addition | + | + |
soustraction | - | - |
multiplication | × | * |
division (*) | div | / ou // (**) |
reste | mod | % |
puissance | 2³ | 2**3 |
(*) euclidienne ou entière
(**) Python 2 ou 3
>>> 147 // 2
73
>>> 147 % 2
1
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
False
, ce qui confirme que 147 est impair.
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
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.
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).
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.
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
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.
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
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]
.
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 »
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
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.
>>> 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]
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
.
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 "
.
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', '{', '|', '}', '~']
Un caractère est toujours Vrai
.
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…).
>>> 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
La même que celle des listes puisque les chaînes de caractères sont des listes.
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.
En cours de rédaction…
Les dictionnaires vides sont False
. Tous les autres sont True
.
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).
Au choix du concepteur de l’objet.
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 :
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 |