0741 Introduction A Lalgorithmique Et A La Programmation Avec Python
0741 Introduction A Lalgorithmique Et A La Programmation Avec Python
Laurent Signac
https://deptinfo-ensip.univ-poitiers.fr
27 septembre 2017
Algorithmique et Programmation avec Python
2
Table des matières
II Algorithmique et programmation 15
5 À quoi sert un algorithme ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
6 Algorithme d’Euclide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
7 Poser les problèmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
8 Types, affectations, expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
9 Fonctions et procédures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
10 Conditions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
11 Boucles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
12 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
13 Notation pointée . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
14 Affectation, mode de passage des paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
15 Récursivité . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
16 Résoudre des problèmes : méthode de travail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
17 Récursivité et Logo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3
Algorithmique et Programmation avec Python
Notations
Dans ce support de cours, les programmes sont généralement typographiés en police machine à écrire et encadrés,
alors que les algorithmes n’ont pas de cadre et utilisent une police sans empattements.
Sauf si une indication contraire est donnée, le langage utilisé est Python. On différencie les «sessions shell» par la
présence du prompt >>>. En l’absence de prompt, il s’agit généralement d’un programme à entrer dans un éditeur de
texte et à exécuter ensuite (l’interface Idle convient très bien pour faire tout ceci).
Introduction
L’informatique, telle que nous la connaissons aujourd’hui résulte à la fois des progrès théoriques, amorcés dans les
années 30 par les travaux de Turing, et des progrès technologiques, liés à l’électronique, avec en particulier l’invention
du transistor dans les années 40.
Depuis, ces deux facettes, science théorique et défis technologiques progressent de conserve.
L’objet de ce cours est de présenter l’ère numérique sous l’angle du codage des informations, puis celle des ordinateurs
sous celui des algorithmes et de la programmation.
Ressources
La dernière version de ce fichier est disponible ici : https://deptinfo-ensip.univ-poitiers.fr/FILES/PDF/python1a.
pdf
Les ressources Web associées à ce cours sont :
— Sur Updago : Algorithmique et Programmation Python
— https://deptinfo-ensip.univ-poitiers.fr/ENS/doku
Les introduction à Python sont nombreuses sur le Web, et certaines sont de très bonne qualité :
— Le cours de Bob Cordeau :
http://perso.limsi.fr/pointal/python:courspython3
— Le cours de Pierre Puiseux :
http://web.univ-pau.fr/~puiseux/enseignement/python/python.pdf
Voici ceux qui ont été utilisés lors de l’écriture de ce cours 1 :
— Le Goff, Vincent, Apprenez à Programmer en Python, Le livre du Zéro
— Puiseux, Pierre, Le Langage Python, Ellipses TechnoSup
— Chazallet, Sébastien, Python 3 – Les fondamentaux du langage, Eni
— Zelle, John, Python Programming, Franklin, Beedle, and Associates
— Lee, Kent D., Python Programming Fundamentals, Springer
— Summerfield, Mark, Programming in Python 3, Addison Wesley, 2e ed.
Les parties du cours qui ne concernent pas précisément Python sont issues de nombreux ouvrages impossibles à tous
mentionner ici et de quelques années de pratique.
Licence
Ce travail est mis à disposition sous licence Creative Commons by nd (paternité, pas de modification).
http://creativecommons.org/licenses/by-nd/3.0
4
Chapitre I
1 Ordinateur ?
Les machines électroniques nous entourent désormais : de la simple calculatrice aux super-ordinateurs, en passant par
les smartphones, les tablettes et les micro-ordinateurs. À partir de quel degré de complexité avons-nous affaire à un
ordinateur ?
Comme nous allons le voir, les ordinateurs actuels sont à peu près calqués sur un modèle datant des années 50, le
modèle de Von Neumann. Si nous devions retenir deux caractéristiques des ordinateurs, ce serait que :
— un ordinateur doit être programmable
— son programme doit être enregistré dans sa mémoire.
Par voie de conséquence, une simple calculatrice de poche n’est pas vraiment un ordinateur, alors que les modèles
programmables plus perfectionnés en sont. De même que les tablettes et les smartphones...
5
Algorithmique et Programmation avec Python
Pourquoi le binaire ?
Il y a une raison théorique et une raison technique à l’utilisation du binaire :
— on ne peut pas faire plus simple que 2 symboles. Avec un seul symbole, plus rien ne fonctionne (on a un seul
codage de longueur fixe, la taille de l’écriture d’un nombre, écrit en unaire est proportionnelle au nombre, et
non pas à son logarithme).
— les deux symboles 0 et 1 sont transposables électroniquement par : le courant passe ou ne passe pas (système
assez robuste)
On représente donc toutes les informations sous forme de bits (binary digit = chiffre binaire). La quantité d’infor-
mation est justement le nombre de bits nécessaires pour représenter cette information.
Unités
— le bit (binary digit) est l’unité d’information : 0 ou 1
— l’octet est un groupe de 8 bits (attention, octet se dit byte en anglais)
Tout le reste.... dépend du contexte. En particulier, le préfixe «kilo» correspond dans le système international au
multiplicateur 103 , mais conventionnellement, il était utilisé en informatique pour le multiplicateur 210 . La vérité 2
est que, maintenant, nous devrions utiliser les préfixes du si pour le multiplicateur 103 (symboles k,M,G...) et
d’autres préfixes (kibi, mébi, gibi,... symboles ki,Mi,Gi...) pour les multiplicateurs 210 , 220 , 230 ...Il en est de même
pour les débits (bits/seconde, puis kbits par seconde etc...). Dans les symboles, le «o» de octet est parfois remplacé
par le «B» de byte, à ne pas confondre avec le «b» de bit...
Enfin, l’explorateur Windows utilise le symbole Ko pour 210 octets (alors que ce devrait être 103 octets) et l’utilitaire
gnu du -k utilise aussi des kilos de 1024 octets (encore qu’il a une option supplémentaire permettant d’utiliser les
kilos du si).
Dans le doute, partez du principe que si on vous vend de la mémoire (disque dur, mémoire flash...) sa taille est
indiqué en utilisant le si...
où v(ci ) est la valeur associée au chiffre (symbole) ci . La valeur du symbole 9 en base 10 est par exemple neuf.
Généralement, pour une base b inférieure à 10, on reprend les même chiffres qu’en base 10, avec les mêmes valeurs
(mais on n’utilise que les b premiers chiffres). Pour une base b supérieure à 10, on ajoute aux chiffres ordinaires d’autres
symboles, comme des lettres. La base 16 utilise par exemple les 16 chiffres suivantes : 0, 1, 2, ..., 9, A, B, C, D, E, F .
Voici quelques exemple, la base est indiquée en indice à la fin du nombre. En l’absence de cet indication, c’est la base
10 qui est utilisée.
1011010|2 = 1 × 26 + 0 × 25 + 1 × 24 + 1 × 23 + 0 × 22 + 1 × 21 + 0 × 20 = 90
5A|16 = 5 × 161 + 10 × 160 = 90
Pour passer de la base 10 à la base 2, on peut procéder par divisions successives (par 2). Voici comment trouver
l’écriture binaire de 90 :
90 = 2 × 45 +0
45 = 2 × 22 +1
22 = 2 × 11 +0
11 = 2 × 5 +1
5 =2×2 +1
2 =2×1 +0
1 =2×0 +1
La séquence des restes successifs, lue du dernier au premier donne : 1011010 qui est l’écriture en base 2 de 90.
On peut procéder de même en base 16 (en faisant des divisions par 16). Les restes sont alors entre 0 et 15, ce qui
correspond bien aux valeurs des chiffres de la base 16.
Les nombres non-entiers sont codés de la même manière. Chaque chiffre placé après la virgule est associé à une
puissance négative de la base :
6
Notes de cours, Ensip 1A
Le passage de la base 10 à la base 2, pour la partie fractionnaire, est réalisé par multiplications successives (on reporte
la partie fractionnaire de chaque résultat sur la ligne suivante, et les parties entières des résultats obtenus forment
l’écriture en base 2). Voici comment écrire 0, 6875 en base 2 :
0, 6875 ×2 = 1, 375
0, 375 ×2 = 0, 75
0, 75 ×2 = 1, 5
0.5 ×2 = 1
Nous prenons les parties entières des résultats dans l’ordre où elles sont obtenues : 1011. Donc 0, 6875 = 0, 1011|2 .
Développement binaires infinis
Notons que si l’écriture d’un nombre non entier en base 2 est finie, ce sera aussi le cas en base 10 (car les puissances
négatives de 2 ont toutes une écriture décimale finie). En revanche, si l’écriture décimale est finie, l’écriture en base
2 ne le sera pas forcément (elle sera alors périodique). Par exemple :
0, 2 ×2 = 0, 4
0, 4 ×2 = 0, 8
0, 8 ×2 = 1, 6
0, 6 ×2 = 1, 2
0, 2 ×2 = ...
En conséquence : 0, 2 = 0, 00110011...
Les nombres négatifs sont représentés en binaire en utilisant la méthode du complément à deux, qui permet de réaliser
simplement des opérations arithmétiques directement sur le codage.
Pour représenter un nombre avec la méthode du complément à 2, on choisit auparavant le nombre de bits maximum
qui sera occupé par le code. Les valeurs typiques sont multiples de 8. Dans la suite, nous utiliserons des compléments
à 2 sur 8 bits.
Coder les nombres en complément à 2 sur 8 bits revient à partager les 256 codes possibles en deux parties. Ceux dont
le bit de poids fort est 0 représenteront les nombres positifs de 0 à 127 (codage immédiat en base 2 avec les 7 bits
restants). Ceux dont le bit de poids fort est 1 représenteront les nombres négatifs de -128 à -1.
Dans le cas d’un nombre négatif n, on obtient le complément à 2 de n en calculant la représentation binaire du nombre
256 − |n|. Cette représentation est obtenue rapidement en écrivant sur 8 bits la représentation binaire de la valeur
absolue de n, puis en inversant les 0 et les 1, puis en ajoutant 1 :
Exemple de codage en complément à deux
Nous souhaitons coder le nombre -66 en complément à deux sur 8 bits. On commence par écrire sa valeur absolue
en binaire, sur 8 bits :
01000010
Puis on échange les 0 et les 1 :
10111101
Puis on ajoute 1 (attention aux éventuelles retenues) :
10111110
Le résultat est le codage en complément à 2 sur 8 bits de −66. On remarque que c’est aussi l’écriture binaire de
256 − 66 = 190.
Virgule fixe
Coder un nombre en virgule fixe consiste à réserver un certain nombre de chiffres pour la partie entière et un certain
nombre de chiffres pour la partie décimale. Ainsi, en codant en virgule fixe sur 2 octetes avec un octet pour la partie
entière, on peut représenter les nombres de 0 à 256 − 2−8 avec un «pas» de 2−8 . Le fait que le pas soit fixe est un
défaut de ce codage.
7
Algorithmique et Programmation avec Python
Virgule flottante
Nous avons vu qu’il était possible de représenter les nombres à virgule en binaire et de les coder par exemple en virgule
fixe. Cependant, l’utilisation très fréquente des calculs à l’aide de nombres non entiers, le soucis de coder ces nombres
en un minimum de bits et la nécessité d’avoir une erreur «relative» peu importante (pas de pas fixe) a donné naissance
au codage en virgule flottante. Ce codage (norme ieee-754) existe essentiellement en deux versions, simple et double
précision, utilisant des codages sur 32 et 64 bits. Le codage en simple précision permet de représenter de manière
approchée les nombres entre −3.4 × 1038 et 3.4 × 1038 , tout en atteignant des valeurs aussi petites (en valeur absolue)
que 1.4 × 10−45 .
Nous ne détaillerons pas ici ce codage, assez technique, mais il est important de retenir :
— que c’est le codage le plus utilisé pour les nombres non entiers ;
— qu’il représente les nombres de manière approchée, et qu’en virgule flottante, les calculs ne sont (pratiquement)
jamais exacts.
Flops
Une indication de vitesse des processeurs donnée en flops (ou un de ses multiples) fait référence au nombre
d’opérations en virgule flottantes par seconde (FLoating point Operations Per Second).
L’ordinateur le plus rapide du monde (depuis 2013 3 , c’est Tianhe II à 3 millions de cœurs) a une vitesse estimée de
30 PetaFlops (1015 ) soit plus de 30 millions de milliards d’opérations en virgule flottante par seconde. Un ordinateur
domestique atteint des vitesses de quelques dizaines de GigaFlops (109 Flops)
Les différents moyens de codage reposent sur un certain nombre de conventions. Par exemple, modifier l’ordre des
couleurs primaires dans le codage des couleurs en vert, bleu, rouge à la place de rouge, vert, bleu modifie bien entendu
les couleurs associées aux nombres. Respecter ces conventions, c’est adopter un standard, et cette adoption facilitera
grandement les échanges de données entre les personnes ou les programmes. En particulier, si le standard est publié,
on dit qu’il est ouvert 4 , par opposition à un standard fermé, qui restreint, de manière légale ou en ne publiant pas les
spécifications du standard, l’écriture d’applications compatibles qui pourraient utiliser les mêmes fichiers de données,
par exemple.
Les standards de représentation et de codage des informations sont extrêmement nombreux. Pour chaque type d’objet
numérique, il existe de nombreux standards : pour le texte (latin, utf, ascii...), pour les documents mis en page (Open
Document Format, Office Open xml...), les images (jpeg, png...), l’audio (mp3, ogg, flac...), la vidéo (mpegv2,
Vorbis, h-264...).
4. Précisément, un standard ouvert est publié mais ne doit pas imposer non plus de restrictions quant à son utilisation.
8
Notes de cours, Ensip 1A
Le code ascii a rapidement été insuffisant, faute de caractères accentués, grecs, cyrilliques, hébreux, chinois...
Deux autres types de codage ont donc fait leur apparition :
— Les extensions rajoutent des caractères à la suite de la table (chaque alphabet possède alors son extension) :
c’est le cas du latin-1 (iso-8859-1). Il y a des extensions pour le chinois, l’hébreu... Il faut donc indiquer quelle
est l’extension utilisée pour pouvoir lire correctement le fichier.
— Unicode est une norme qui recense tous les caractères existant dans une table (il y en a plus de 100 000
actuellement). Les codes sont ensuite représentés dans un codage particulier comme utf-8, ou utf-16. C’est
ce type de codage qu’il convient d’utiliser maintenant.
Le texte peut aussi être enrichi : changement de la taille des caractères, de la graisse, utilistion de l’italique... Là
aussi, il existe une multitude de manières de représenter du texte enrichi. Nous pouvons citer le format Rtf (Rich
Text Format), le Html (HyperText Markup Language), et dans une certaine mesure les formats issus de logiciels de
bureautique.
Voici l’extrait d’un fichier html :
<html>
<body>
<h1>Algorithmique</h1>
<p>
L’<b>algorithmique</b> est l’ensemble des règles et des techniques
qui sont impliquées dans la définition et la conception
d’<a href="http://fr.wikipedia.org/wiki/Algorithme">algorithmes</a>,
c’est-à-dire de processus systématiques de résolution
d’un problème permettant de décrire les étapes vers le résultat.
En d’autres termes, un algorithme est une suite finie et non-ambiguë
d’instructions permettant de donner la réponse à un problème.
</p>
</body>
</html>
Le texte enrichi précédent, issu de Wikipédia, contient un paragraphe, avec un titre de niveau 1, un mot en gras, et
un lien hypertexte vers une autre page Wikipédia. Tous ces éléments sont assez faciles à repérer. Le navigateur Web
les interprête pour nous et affiche une page avec le texte mis en forme.
2.5 Sons
Le codage d’un son, ou d’une courbe en général, est réalisé en échantillonnant dans le temps le signal continu. Chaque
échantillon est représenté par un nombre, avec une certaine précision (souvent exprimée en bits). Ce codage se nomme
pcm (Pulse Code Modulation).
Selon la nature du signal ainsi numérisé, il est possible d’utiliser des codages plus économes en mémoires (compression),
comme Flac (compression sans perte) ou mp3 (compression avec pertes).
9
Algorithmique et Programmation avec Python
Compact-disc Audio
Sur un compact-disc audio, les données ne sont pas compressés, et ce sont les échantillons qui sont directement
représentés. Les enregistrements sont stéréo (deux signaux), échantillonnés à 44100 Hz (pour conserver les fréquences
jusqu’à 20kHz), chaque échantillon étant représenté sur 16 bits.
2.6 Images
Un image numérique (telle que celle stockée dans les appareils photos) est une matrice de pixels. Chaque pixel est un
point indivisible en couleur de l’image. Les éléments importants en matière de qualité de l’image sont donc : la taille
de la matrice, et le nombre de bits utilisés pour coder la couleur de chacun des pixels.
Typiquement, chaque pixel est codé sur 24 bits (8 bits par composante) et un appareil photo annonçant 12 millions
de pixels réalise des photographies de 4000 sur 3000 pixels.
Résolution
La résolution en dpi (Dots Per Inch) fait référence au nombre de pixels d’une image rapporté à la taille physique
(sur un écran ou sur papier). On estime qu’une impression est très correcte (pour des besoins ordinaires) au delà
de 300 dpi, c’est à dire au delà de 300 pixels par pouce. La taille maximum à laquelle pourra être imprimée une
photo prise avec un appareil à 12 millions de pixels, tout en satisfaisant cette contrainte de qualité, sera donc
(1 pouce = 2, 54 cm) :
4000/300 × 2, 54 ≈ 33, 8 cm
c’est à dire environ 25 centimètres sur 34.
Formats d’image
Si la compréhension de la numérisation d’une image est assez simple, le stockage de l’image numérique est réalisé
dans un des multiples formats disponibles. Comme pour le son, ces codages ont généralement pour objectif de réduire
la taille des fichiers de stockage. La compression réalisée peut être faite avec perte (c’est le cas du format jpeg) ou
sans perte (c’est le cas du format png). Le nombre de couleurs simultanément présentes peut être de 16,7 millions
(codage sur 24 bits sans palette) ou moins (comme avec le format gif). Ainsi, le choix d’un format d’image peut
dépendre du type d’image à coder (on utilise facilement jpeg pour des photos, et le format png est préférable pour
des dessins au trait).
5. Pour une image, cette étape s’appelle la rasterisation et elle est effectuée chaque fois qu’on affiche l’objet vectoriel sur un écran.
10
Notes de cours, Ensip 1A
3 Langages de programmation
Science is what we understand well enough to explain to a computer.
Art is everything else we do.
Donald E. Knuth
Un langage de programmation permet d’écrire un programme. Un programme, lorsqu’il est sous la forme de code
source, est un texte qui exprime un algorithme (nous y reviendrons), en vue de permettre l’exécution de ce dernier sur
une machine.
Les langages utilisés pour programmer sont situés quelque part entre les séquences de 0 et 1 chères à la machine et le
langage naturel cher à l’humain.
Pourquoi choisir un langage ?
L’informatique sur papier est possible, mais elle est moins distrayante que sur machine. De plus, la réalisation d’un
programme est le moyen d’obtenir des réponses effectives aux problèmes qui nécessitent l’utilisation d’un ordinateur.
Enfin, la programmation est une activité de création et de rigueur très formatrice, et elle aide à comprendre la
manière dont fonctionnent les algorithmes.
Pseudo-code
Le pseudo-code est utilisé dans de nombreux ouvrages d’algorithmique (ci-dessous, l’exponentiation modulaire don-
née dans le clr 6 , et légèrement remise en page) :
Exponentionation_modulaire(a,b,n)
c←0
d←1
soit < bk , bk−1 , ..., b0 > la représentation binaire de b
pour i ←k à 0 faire
c←2c
d←(d.d) mod n
si bi =1 alors
c←c+1
d←(d.a) mod n
retourner d
Il s’agit d’un algorithme rédigé dans un langage assez naturel (plutôt qu’un langage de programmation), mais qui
colle tout de même aux constructions habituelles des langages (tests, boucles...). Dans ce cours, nous n’utiliserons pas
particulièrement de pseudo-code. Il est cependant tout à fait légitime de résoudre les exercices proposés en écrivant
du pseudo-code, ou même en mélangeant du code dans un langage particulier et du pseudo-code. L’inconvénient
essentiel est qu’alors on ne peut pas tester nos algorithmes...
Il existe des centaines de langages différents, qui peuvent être plus ou moins spécialisés pour certaines tâches, qui per-
mettent d’utiliser différents paradigmes de programmation (impératif, objet, par contraintes, logique...). Les langages
peuvent être plus ou moins verbeux, plus ou moins expressifs. Certains langages sont très répandus, ou au contraire
ne sont utilisés que par une poignée de personnes. Les langages peuvent être jeunes ou vieillissant, et, lorsque le côté
affectif s’en mêle, ils peuvent être agréables à utiliser ou au contraire agaçants...
La tâche qui consiste à choisir un langage de programmation pour illustrer un cours d’algorithmique et d’informatique,
à destination d’un public hétérogène est donc... difficile. Dans la suite, c’est le langage Python qui sera utilisé. Il est :
généraliste, assez répandu (la pente étant dans le bon sens...), multi-paradigmes, assez jeune, expressif et agréable à
utiliser. Il n’est cependant pas le seul à posséder toutes ces qualités.
D’un point de vue plus technique, Python est aussi un langage interprété (par opposition à un langage compilé). En
partie pour cette raison, il est d’apprentissage rapide, il est portable, mais il est en revanche lent à l’exécution.
Enfin, il existe plusieurs interpréteurs Python, et l’interpréteur standard, nommé CPython, est libre, gratuit et multi
plates-formes.
Voici un exemple de programme écrit en Python :
def fibonacci(n):
f = [0, 1]
while len(f) < n + 1:
f.append(f[-1] + f[-2])
11
Algorithmique et Programmation avec Python
return f[n]
def main():
n = input("Entrez un nombre entier :")
n = int(n)
f = fibonacci(n)
print("Suite de Fibonacci : U_{}={}\n".format(n, f))
Égalité 6= affectation
Attention à ne pas confondre le symbole =, utilisé comme symbole d’affectation dans la plupart des langages avec
le = mathématique qui indique que deux valeurs sont égales. L’équation mathématique a = a + 1 est par exemple
(dans la plupart des contextes) sans intérêt, alors que la ligne de programme a=a+1 indique qu’il faut rajouter 1 au
contenu de a. Non seulement ce n’est pas absurde, mais c’est une écriture très courante.
La plupart des programmes n’ont pas un unique fil d’exécution possible. Selon les valeurs données en entrées, ou selon
le résultat de certains calculs, une chose plutôt qu’une autre doit être faite (par exemple, pour un programme de
recherche de racines d’un polynôme de degré 2, après que l’utilisateur a entré les coefficients, la portion de programme
à exécuter ne sera pas la même selon que le discriminant est positif, négatif ou nul).
Dans l’exemple suivant, selon que la température est en dessous de 0◦ C ou non, un message différent sera affiché :
Lors de la conception d’un programme un peu long, il est indispensable de le structurer en parties indépendantes.
Il est très utile de commencer à prendre cette habitude même avec des programmes courts. Dans l’exemple qui suit,
nous avons écrit une fonction, nommée conversion, qui convertit une température, donnée en degrés Fahrenheit en
une température exprimée en degrés Celcius. La fonction est ensuite utilisée dans le programme principal :
# Fonction
def conversion(f):
c=(f - 32) / 1.8
return c
# Programme principal
fare = float(input("Température en degrés Fahrenheit "))
celc = conversion(fare)
print("En degrés Celcius, cela fait : ", celc)
Le propre de l’ordinateur est de ne pas se plaindre si on lui demande de recommencer de nombreuses fois les mêmes
opérations (avec quelques petites variantes). La structure de boucle permet d’exprimer les répétitions et nous l’utilisons
ci-dessous pour afficher une table de conversion de degrés Fahrenheit vers degrés Celcius (en vue de l’imprimer par
exemple).
12
Notes de cours, Ensip 1A
# Fonction
def conversion(f):
c = (f - 32) / 1.8
return c
# Programme principal
fare = 0
while fare < 100:
celc = conversion(fare)
print(fare, " / ", celc)
fare = fare + 10
13
Algorithmique et Programmation avec Python
14
Chapitre II
Algorithmique et programmation
1. La même manière de trier une liste, d’ajouter deux nombres ou de compresser un fichier peut s’exprimer dans différents langages de
programmation. Il existe donc un objet abstrait, qui s’incarne dans ces programmes écrits dans différents langages. C’est cet objet que l’on
appelle un algorithme – extrait de Introduction à la Science Informatique
15
Algorithmique et Programmation avec Python
6 Algorithme d’Euclide
Commençons par un exemple historique célèbre : l’algo-
rithme d’Euclide.
Algorithme d’Euclide
Étant donnés deux entiers, retrancher le plus petit au plus
grand et recommencer jusqu’à ce que les deux nombres
soient égaux. La valeur obtenue est le plus grand diviseur
commun.
Exercice
Appliquez l’algorithme d’Euclide (à la main) aux nombres
133 et 49
Figure II.1 – Les éléments, livre VII, édition de 1632
Voici les différentes étapes vers la rédaction propre d’un al-
gorithme. Nous partons de l’idée de départ et la raffinons par
étapes...
16
Notes de cours, Ensip 1A
// Code Java
public static int pgdc(int a, int b) { (* Code OCaml *)
while (a!=b) { let rec pgcd(a, b) =
if (a>b) a=a-b; if a = b then a else
else b=b-a; if a > b then pgcd(a-b, b)
} else pgcd(a,b-a)
return a; ;;
}
17
Algorithmique et Programmation avec Python
8.2 Variables
Les variables peuvent être vus comme des espaces de stockage (métaphore du tiroir) ou comme des noms associés à
des objets (étiquettes). Le modèle qui correspond à Python est celui de l’étiquette. Une variable possède plusieurs
caractéristiques :
identificateur (nom) Il doit être bien choisi, surtout dans un programme long. Un identificateur ne comporte
aucun espace ou caractère spécial et ne peut pas être un mot clé. Chaque langage dispose de règles (différentes)
très précises décrivant la syntaxe des identificateurs
type Dans certains langages, le type d’une variable peut changer en cours d’exécution du programme (Python,
Ruby). Dans d’autres, le type est fixé une fois pour toutes (Java, C).
valeur C’est l’objet/valeur désigné par la variable.
portée On ne peut utiliser le nom d’une variable que dans la fonction ou la procédure (en réalité le bloc) qui la
contient 3 . La durée de vie de la variable correspond à la durée d’exécution de la fonction ou de la procédure
(du bloc). Sans ce principe, les programmes longs deviendraient incompréhensibles et difficiles à déverminer.
Attention à l’affectation
Nous l’avons déjà mentionné, mais nous rappelons qu’il ne faut pas confondre l’affectation et l’égalité. L’affectation
est une opération typiquement informatique. En Python elle permet d’affecter un nouveau nom (celui indiqué à
gauche du =) à un objet (indiqué à droite du =). L’opérateur d’affectation n’est donc pas symétrique.
18
Notes de cours, Ensip 1A
>>> a = 19.6
>>> sommeht = 245
>>> sommettc = sommeht * (1 + a / 100)
>>> print(sommettc)
Exercice
La session shell suivante affiche deux prix ht et ttc : 1000 et 1196, puis 2000 et 1196... Il y a manifestement une
erreur. La comprenez-vous ?
L’affectation en Python
En Python, l’affectation a un sens un peu particulier qui pourra vous conduire à de mauvaises surprises. Nous y
reviendrons par la suite, mais les impatients peuvent partir d’un principe que les données ont une existence en
mémoire et que les variables sont des références vers ces données en mémoire.
L’opération d’affectation : x=DATA finalise la création en mémoire de l’objet DATA, puis stocke dans x une référence
vers cet objet.
En particulier, une affectation en Python ne duplique jamais un objet, mais donne simplement un nouveau nom
à un objet existant.
Le comportement observé est la plupart du temps le même que celui des autres langages... mais pas toujours.
8.3 Expressions
Nous utiliserons une définition «intuitive» des expressions, en retenant que c’est une portion de code, plus ou moins
longue, qui peut être évaluée. La valeur obtenue peut par exemple être affectée à une variable.
— 3*i+5 : ok si i est un nombre
— pgdc(105,i)+3 : ok si i est un entier, et si pgdc est une fonction retournant un nombre.
On utilise les notations les plus classiques possibles : +, -, *, /, % (modulo), ** (puissance)
Division entières
Attention, en Python 3 (mais pas en Python 2), la division entre deux entiers, notée /, donne un flottant (même
si la division tombe juste). La division entière est en revanche notée //. Ce comportement est différent de ce qu’on
trouve dans la plupart des langages, pour lesquels c’est le type des opérandes qui fixe le type du résultat.
8.4 Listes
Un tableau, tel que ceux utilisés en C par exemple, permet de stocker selon une ou plusieurs dimensions des données
homogènes (on parle de tableau d’entiers par exemple).
Si le type array existe aussi en Python (module array), on utilise plus volontiers dans les langages de scripts modernes
le type list qui permet de stocker des objets hétérogènes (voire d’autres listes).
Voici un exemple d’utilisation de listes :
19
Algorithmique et Programmation avec Python
>>> l1.append(56)
>>> l2.append(’Fin’)
#Connaître le nombre d’éléments
>>> len(l1)
>>> len(l2)
8.5 Tuples
Les tuples 4 de Python sont des listes non modifiables 5 .
Il n’est pas strictement nécessaire d’utiliser des tuples. Mais certaines méthodes ou fonctions Python en renvoient et
il faut donc connaître leur existence.
9 Fonctions et procédures
9.1 Fonctions
Les fonctions et procédures sont la pierre angulaire de la programmation procédurale.
Il y aura deux points fondamentaux à retenir dès la fin de lecture de cette sous-section :
— comprendre pourquoi on écrit des fonctions ;
— s’obliger à le faire dès les premiers programmes.
Commençons par un exemple en Python :
def calculttc(val, taux):
"""
Calcule le montant ttc, connaissant la somme
hors taxe (val) et le taux (par ex 19.6)
"""
ttc=val * (1 + taux / 100)
return ttc
Instruction return
Que ce soit en langage algorithmique, en C, en Octave ou en Python, l’instruction retourner (return) fait se
terminer la fonction ou la procédure (dans le cas d’une procédure, return n’est pas suivi d’une valeur), même si
cette instruction est exécutée avant la fin du texte de la fonction.
Une fois la fonction calculttc lue par le shell, il est possible de l’utiliser ainsi :
>>> calculttc(1000, 19.6)
=> 1196
20
Notes de cours, Ensip 1A
// En langage C
float calculttc(float val,float taux)
{
float ttc;
ttc=val*(1+taux/100);
return ttc;
}
# En Ruby
def calculttc(val,taux)
val*(1+taux/100)
end
// En Octave
function ttc=calculttc(val,taux)
ttc=val*(1+taux/100)
9.3 Procédures
Il ne faut pas confondre procédure et fonction, même si leur définition, en Python par exemple, est similaire :
— Une fonction calcule. Elle vaut quelque chose (exemples : pgdc et calculttc)
— Une procédure n’a pas de valeur 6 , mais elle fait/modifie quelque chose (exemple : afficher à l’écran, modifier
un objet). On dit qu’elle a un effet de bord (elle modifie quelque chose qui est en dehors de la procédure).
Même si la différence fonction/procédure n’est pas syntaxique (au niveau de la ligne de prototype) en Python, elle est
très importante :
On pourrait penser que la fonction foncttc et la procédure procttc sont équivalentes 7 . Ce serait une erreur. La
fonction est plus générale. On ne peut pas écrire par exemple :
En revanche, la même ligne, en utilisant foncttc fournirait le bon résultat. Assurez-vous d’avoir bien compris pourquoi
la ligne qui précède marche avec une fonction et pas avec une procédure. C’est vraiment très important.
Effets de bords
Autant que possible, on souhaite qu’une fonction n’ait pas d’effet de bord, d’aucune sorte :
— pas de modification d’un objet qui n’appartient pas à la fonction (variable globale par exemple, ou objet
passé par référence en paramètre) ;
6. En Python, une procédure renvoie la valeur spéciale None.
7. Si on entre procttc(100,7) ou foncttc(100,7) dans le shell, on verra une réponse correcte s’afficher (quoique un peu différemment)
dans les deux cas...
21
Algorithmique et Programmation avec Python
— pas d’entrées/sorties ;
— ...
⇒ on évite donc les affichages dans une fonction (ce n’est pas interdit... c’est déconseillé) : une fonction calcule un
résultat, le renvoie, et on l’affiche ensuite, hors de la fonction.
Le prototype Python n’indique pas les types des entrées, et nous ne pouvons pas savoir si nous avons affaire à une
fonction ou à une procédure.
def aucarre(u):
v = u ** 2
return v
Pour utiliser la fonction qui précède, il est inutile de connaître les noms de variable u et v. Ce qui est important, c’est
que la fonction prend en paramètre un nombre et renvoie un nombre (le carré du premier) :
>>> a = 9
>>> b = aucarre(a)
>>> print(a, b)
9 81
La notion de portée des variables (variables locales, globales) est souvent mal comprise au début : elle peut être perçue
comme une difficulté, voire un défaut, alors qu’elle simplifie grandement le travail du programmeur.
Local, englobant, global et built-in
Les auteurs anglophones conseillent de retenir la règle legb, acronyme qui permet de retenir l’ordre de résolution
des noms de variable dans Python : local, englobant, global et built-in. Les variables locales à une fonction sont
celles définies dans la fonction ainsi que ses paramètres. Les variables du bloc englobant d’une fonction sont celles
dont la portée contient la fonction. Les variables globales sont celles déclarées au niveau du fichier python. Enfin,
built-in regroupe ce qui est défini à l’exécution de l’interpréteur.
Dans un premier temps, il est nécessaire de :
— ne pas utiliser de variable globale (ce qui est une bonne chose de toutes façons) ;
— bien comprendre qu’une variable locale à une fonction n’existe pas en dehors de cette fonction.
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as
22
Notes de cours, Ensip 1A
cleverly as possible, you are, by definition, not smart enough to debug it.
Brian Kernighan
Tester un programme
Une fois le programme écrit, il faut vérifier qu’il est juste. Pour cela, on le teste sur des valeurs particulières. Le
choix de ces valeurs est important. Il faut :
— tester avec des entrées pour lesquelles on connaît la sortie correcte ;
— tester de manière extensive, afin que chaque portion de code soit exécutée ;
— penser aux cas extrêmes qui posent parfois des problèmes.
1. Une fonction vaut quelque chose, une procédure fait quelque chose.
2. Le prototype (déclaration) d’une fonction suffit (normalement) à savoir ce qu’elle calcule et comment on
l’utilise, mais pas comment elle le calcule.
3. On écrit des fonctions et des procédures pour ne plus avoir à se soucier de la façon de régler le problème
qu’elles traitent. En conséquence, être obligé de retoucher le code d’une fonction pour un problème particulier
indique généralement que la fonction a été mal conçue au départ.
4. Généralement, lorsqu’on a terminé l’écriture d’une fonction et qu’on l’a testée, on souhaite ne plus avoir à
revenir dessus et ne plus devoir la modifier.
http://xkcd.org
23
Algorithmique et Programmation avec Python
10 Conditions
Une mère dit à son fils : «Va au marché et achète une bouteille de lait. S’il y a des oeufs,
prends-en six.»
Le fils revient avec six bouteilles de lait.
Sa mère lui demande : «Mais pourquoi as-tu ramené six bouteilles de lait ?»
Le fils répond : «Parce qu’il y avait des oeufs !»
Le cyberblog du coyote
condition
Nous souhaitons demander un nombre à l’utilisateur et
Vrai afficher un message particulier si ce nombre est un mul-
instructions tiple de 13 :
Faux
du bloc alors
n = int(input(’Entrez un nombre :’))
instructions if n % 13 == 0:
après le test print(n, ’est divisible par 13’)
print(’Bravo’, n)
print("Merci d’avoir participé")
Exercice
Identifiez chaque partie du programme qui précède avec les blocs correspondant de l’organigramme.
instructions
après le test
Pour chaque valeur de u0 choisie, on obtient une nouvelle séquence de nombres. Écrivons une fonction qui calcule un+1
en fonction de un .
fonction collatz (u : entier) : entier
res : entier
si u est pair
res ←u/2
sinon
res ←3∗u+1
retourner res
24
Notes de cours, Ensip 1A
def collatz(u):
if u % 2 == 0:
res = u // 2
else :
res = 3 * u + 1
return res
Exercice
Identifiez chaque partie du programme qui précède avec les blocs correspondant de l’organigramme.
Opérateurs booléens
Ce sont les classiques : and, or, not.
Attention aux priorités :
True or True and False vaut True
(True or True)and False vaut False
True or (True and False) vaut True
11 Boucles
Une mère dit à son fils :
«Va au supermarché acheter une bouteille de lait. Et tant que tu y es, prends des oeufs.»
Il ne revint jamais.
linuxfr.org
Exercice
Identifiez chaque partie du programme qui précède avec les blocs correspondant de l’organigramme.
Voici un autre exemple de boucle : la suite de collatz, que nous avons récemment rencontré a la propriété, quel que
soit son nombre de départ, de toujours terminer sur le cycle 1, 2, 4. Ce fait a été vérifié expérimentalement jusqu’à de
25
Algorithmique et Programmation avec Python
très grandes valeurs, mais a pour l’instant le statut de conjecture car la preuve semble échapper aux mathématiques
actuelles.
Voici une fonction qui renvoie une liste contenant la suite issue d’un nombre n, jusqu’à ce que la valeur 1 soit atteinte.
Cette liste s’appelle le vol n :
def suite(depart):
s = []
val = depart
while val != 1 :
s.append(val)
val = collatz(val)
s.append(1)
Exercice
Identifiez chaque partie du programme qui précède avec les blocs correspondant de l’organigramme. Profitez de
la présence d’une machine près de vous pour vous émerveiller devant la longueur du vol 270. Quel est son point
culminant ?
Python n’est pas en reste pour ce type de boucle, même si la construction offerte est plus générale que la simple boucle
avec compteur.
La traduction de l’algorithme qui précède en Python donnerait :
La boucle for de Python permet en fait d’itérer sur n’importe quel objet itérable. Or l’objet range(a,b) est itérable
et «contient» les entiers de a à b-1 inclus. C’est pourquoi dans l’exemple qui précède i prend successivement toutes
les valeurs de 1 à 10.
Avec la boucle for de Python, il est possible de traverser toutes sortes d’objets :
1. Dans la première boucle, nombre prendra successivement toutes les valeurs de la liste.
2. Une chaîne est itérable. Dans la seconde boucle, lettre vaudra successivement chaque lettre de la chaîne de
caractères
3. La fonction enumerate sur un itérable renvoie un nouvel itérable dont les éléments sont des tuples à 2
éléments : le premier est un numéro d’ordre et le second l’élémént dans l’itérable de départ. Par exemple
enumerate(’Hello’) correspond à [(0, ’H’), (1, ’e’), (2, ’l’), (3, ’l’), (4, ’o’)]. Cette boucle
aurait pu être écrite :
26
Notes de cours, Ensip 1A
Inspecter les objets itérables En première lecture, vous pouvez omettre ce paragraphe.
Depuis Python 3, il n’est pas toujours immédiat d’inspecter les valeurs d’un objet itérable :
>>> s = "analphabète"
>>> l = enumerate(s)
>>> l
<enumerate object at 0x7feecc10cb40>
>>> list(l)
[(0, ’a’), (1, ’n’), (2, ’a’), (3, ’l’), (4, ’p’), (5, ’h’), (6, ’a’), (7, ’b’), (8, ’è’),
(9, ’t’), (10, ’e’)]
Mais attention, l’objet l est générateur qui ne peut être traversé qu’une fois (et le transformer en liste implique qu’on
le traverse) :
>>> list(l)
[]
>>> l = enumerate(s)
>>> list(l)
[(0, ’a’), (1, ’n’), (2, ’a’), (3, ’l’), (4, ’p’), (5, ’h’), (6, ’a’), (7, ’b’), (8, ’è’),
(9, ’t’), (10, ’e’)]
12 Commentaires
Les commentaires sont signalées par un # en Python. Tous les caractères qui suivent ce symbole sont ignorés par
l’interpréteur.
Les commentaires servent à aider le lecteur humain à comprendre le programme. Il n’en faut pas trop, et ils doivent
être judicieux (on ne commente pas ce qui est évident).
# Initialise s à 1
s={1}
# Met dans s les diviseurs
# Pour i variant de 2 à n-1
# stricts de n
for i in range(2,n) :
s={1}
# si le reste de la
for i in range(2,n) :
#division de n par i est nul
if n % i == 0 :
if n % i == 0 :
s.add(i)
# ajouter i à s
s.add(i)
13 Notation pointée
Bien que ce support de cours n’ait pas pour but d’enseigner la programmation orientée objets, il est nécessaire d’en
comprendre certaines notations afin de bien utiliser Python.
Un objet est une entité composée d’attributs (typiquement des valeurs) et de méthodes (les actions que l’objet peut
effectuer). C’est le cas de la tortue du module turtle. Un objet de type Turtle encapsule une position, une orientation,
la taille du trait à tracer, la couleur à utiliser etc... Tout ceci est stocké dans l’objet. On peut par exemple disposer de
deux tortues, l’une écrivant en rouge et l’autre en bleu.
Les méthodes sont les actions que peut effectuer la tortue : avancer (forward), tourner à droite (right)...
27
Algorithmique et Programmation avec Python
Les objets offrent un plus grand niveau d’abstraction : Si la tortue est en (0,0), orienté selon l’angle 45, et que nous
lui ordonnons d’avancer de 10, le calcul de la nouvelle position est fait dans l’objet : Nous donnons des ordres de haut
niveau et l’objet gère ses détails interne.
Les ordres donnés à des objets (les appels de méthodes donc) utilisent la notation pointée. Pour faire avancer une
tortue t1 de 50 par exemple, nous indiquerons t1.forward(50) qui signifie : appeler la méthode forward de l’objet
t1 et lui transmettre le paramètre 50. La fonction help de python, lorsqu’on lui donne un objet en paramètre, indique
ainsi l’ensemble des attributs et des méthodes de l’objet en question.
Voici un exemple d’utilisation du module turtle :
l = [1, 2, 3, 4]
l.append(5) # ajoute 5 à la liste l
...
Dans l’exemple qui précède, l est une référence vers un objet, instance de la classe list. Cet objet contient des données
(les références vers les objets stockés dans la liste, et d’autres choses encore) ainsi que des méthodes, que l’objet peut
appliquer.
La programmation orientée objets ne se résume pas à ça bien sûr, mais ce qui précède est l’essentiel à savoir pour
utiliser les objets.
Intéressons-nous au problème suivant : Si on envoie une variable en paramètre à une fonction ou une procédure, et
qu’on la modifie dans la fonction, est-elle réellement modifiée après l’appel ?
# Procédure ajoute
def ajoute(a):
a = a + 1
# Programme principal
b = 5
ajoute(b)
print(b)
28
Notes de cours, Ensip 1A
b = 5
# exécution de ajoute(b) :
a = b
a = a + 1
# retour au prog principal
print(b)
Sur cet exemple, il est évident que le programme affichera 5 (c’est a qui contient 6, pas b).
Le mécanisme responsable de ce comportement est plus complexe qu’il n’y parait, et est assez spécifique à Python.
Tout se passe comme si le paramètre était passé par valeur : une copie du contenu de la variable est recopié de b vers
a et c’est la copie qui est modifiée, pas l’original.
Pour comprendre le mécanisme réel, détaillons les deux lignes de code suivantes :
a = 5
c = a + 1
Nous avons déjà mentionné que les variables, en Python, étaient des références vers des objets. Cette nuance est
maintenant centrale. La figure suivante illustre la manière dont les lignes précédentes, et tout particulièrement la
seconde, sont exécutées 8 :
1 2
a a a c
5 5 6 5 6
b = 5
# exécution de ajoute(b) :
a = b
a = a + 1
1 2 3 4
a a
b b b b a
5 5 5 6 5 6
1 b=5 : on crée l’objet 5, et b est une nouvelle référence vers cet objet
2 a=b : on a une simple référence à droite du symbole =, a devient une nouvelle référence vers l’objet b, qui existait
déjà.
3 et 4 a=a+1 : on crée l’objet spécifié à droite du = (6), et a devient une référence vers ce nouvel objet.
8. Dans cette figure, les objets sont représentés par des carrés ou des rectangles, et les références par des cercles. Nous conserverons
cette convention par la suite.
29
Algorithmique et Programmation avec Python
# Procédure ajoute_liste
# ajoute v à la fin de la liste l
def ajoute_liste(l, v) :
l.append(v)
# Programme principal
lst = [1, 2, 3]
ajoute_liste(lst, 42)
print(lst)
Nous posons toujours la même question : que va afficher le programme principal : [1,2,3] ou [1,2,3,42].
Là aussi (ceci est toujours valable), le passage des paramètres est comparable à une affectation. Aussi, la machine
exécute :
lst = [1, 2, 3]
# exécution de ajoute_liste(lst,42)
l = lst
v = 42
l.append(v)
# retour au prog principal
print(lst)
Cette fois-ci, c’est [1,2,3,42] qui va s’afficher, comme si la procédure ajoute_liste avait pu modifier la liste originale,
de manière contradictoire avec ce qui se passait précédemment avec la procédure ajoute et un nombre entier...
Dans cet exemple, tout se passe comme si la liste était passée par adresse. C’est donc la liste originale qui est modifiée
dans la procédure.
Examinons dans un premier temps ce qui se cache derrière la simple ligne :
lst = [1, 2, 3]
Lors de l’exécution de cette instruction, les objets 1, 2 et 3 sont créés, suivis d’une liste qui contient les références vers
chacun des objets : les élément de la liste ne sont pas les objets mais des références vers ces objets. Cette liste est enfin
référencée par lst. On se trouve dans cette situation :
lst
1 2 3
Voyons maintenant ce qui se cache derrière le passage de paramètres de notre second exemple :
lst = [1, 2, 3]
# exécution de ajoute_liste(lst,42)
# on fait grâce du passage de 42...
l = lst
l.append(42)
1 2 3 1 2 3 1 2 3 42 1 2 3 42
30
Notes de cours, Ensip 1A
1 lst=[1,2,3] : la liste lst est créée, et contient des références vers 3 entiers.
2 l=lst : une nouvelle référence à la liste est ajoutée
3 et 4 l.append(42) : l’objet 42 est créé. Puis, la méthode append demande à la liste référencée par l de stocker
une référence vers l’objet passé en paramètre.
Dans ce cas, la liste d’origine (celle qui était désignée par lst est bel et bien modifiée. Ceci explique que la liste du
programme principal, qui contenait [1,2,3], contient effectivement quatre éléments après l’appel à ajoute_liste.
# Programme principal
lst=[1, 2, 3]
ajoute_liste2(lst, 42)
print(lst)
Le passage des paramètres est toujours équivalent à une affectation et tout se passe comme si nous exécutions :
lst = [1, 2, 3]
# exécution de ajoute_liste2(lst, 42)
l = lst
v = 42
l = l + [v]
# retour au prog principal
print(lst)
1 2 3 1 2 3 1 2 3 42
4 5
l
lst lst
1 2 3 42 1 2 3 42
31
Algorithmique et Programmation avec Python
1 lst=[1,2,3] : la liste lst est créée, et contient des références vers 3 entiers.
2 l=lst : une nouvelle référence vers la même liste est ajoutée
La ligne suivante : l=l+[42] correspond, comme ça a été le cas jusqu’à présent, à la création de l’objet à droite du
signe =. Puis l devient une référence vers ce nouvel objet.
La création de l’objet l+[42] est la création d’une nouvelle liste, concaténation de deux listes (l et [42] 9 , de la même
manière que 3+5 provoquerait la création d’un nouvel entier, somme de 3 et de 5.
Résumé
Lorsqu’on passe des objets en paramètres d’une procédure, il est nécessaire de savoir si les modifications qu’on leur
apporte dans la procédure altèrent l’objet d’origine. Les cas simples sont réglés facilement par habitude (nombres
entiers, simples listes), mais lorsqu’on hésite (liste de listes par exemple), une solution est de revenir au modèle
d’affectation de Python, qui permet dans tous les cas d’expliquer ce qui se produit.
15 Récursivité
recursivité : n.f.
1. Voir : récursivité
Voici quelques éléments qui sont toujours présents dans une définition récursive :
— l’objet de taille n est défini à partir d’un objet de taille plus petite (fact(n) est défini à partir de fact(n − 1)).
Ce point n’est pas toujours évident.
— les petits objets ne sont pas définis de manière récursive (c’est le cas de fact(0) dans notre exemple).
9. C’est pour que [42] soit bien une liste et pas un nombre qu’il y a des crochets.
32
Notes de cours, Ensip 1A
Wikimedia, Fibonacci2.jpg
def fibo(n):
if n < 2:
return n
return fibo(n - 1) + fibo(n - 2)
Wikimedia, Liber_abbaci_magliab_f124r.jpg
Il est possible d’écrire un algorithme récursif, très concis, qui indique la liste des déplacements à réaliser pour un
nombre quelconque de disques. Exemple pour 3 disques : 1 → 3, 1 → 2, 3 → 2, 1 → 3, 2 → 1, 2 → 3, 1 → 3.
Cet exercice sera corrigé en cours...
10. du collège Li-Sou-Stian
33
Algorithmique et Programmation avec Python
Analysez le problème Comprenez le problème à résoudre et sa nature. Essayez d’en apprendre le plus possible sur
lui. Vous ne pourrez pas le résoudre avant de le connaître parfaitement.
Spécifiez le programme Décrivez très exactement ce que votre programme va faire. Vous ne devez pas déjà vous
soucier de la façon dont il va le faire, mais plutôt décider très exactement ce qu’il va faire. Pour des programmes
simples, cela consistera à décrire ce que seront les entrées et les sorties du programme et ce qui les relie.
Concevez le programme Formulez la structure globale du programme. C’est à ce moment que vous traiterez de la
façon dont le programme résoudra le problème. Le travail principal est de concevoir le ou les algorithmes qui rempliront
les tâches préalablement spécifiées.
Codez Traduisez les algorithmes conçus dans un langage de programmation et entrez les sur un ordinateur. Dans
ce livre, nous programmerons nos algorithmes en Python.
Testez/Débuggez le programme Essayez votre programme et voyez s’il fonctionne comme vous le souhaitiez. S’il
y a des erreurs (souvent appelées bugs), revenez à l’étape précédente et corrigez les. L’activité qui consiste à débusquer
et corriger les erreurs s’appelle le debuggage 12 . Durant cette phase, votre objectif est de trouver des erreurs, et pour
cela, vous devez tester tout ce qui vous passe par le tête et qui pourrait planter le programme. Il sera utile de garder
à l’esprit la maxime : Nothing is foolproof because fools are too ingenious 13
Faites de la maintenance Continuez à développer votre programme en répondant aux besoins des utilisateurs. La
plupart des programmes ne sont jamais vraiment finis ; ils continuent d’évoluer au fil des années d’utilisation.
-5 -2 3 6 7 9
La première étape consiste à avoir une idée sur la façon de procéder. Le problème n’étant pas très difficile, nous
pouvons envisager une première solution :
Commencer sur la première case. Tant que la case examinée est strictement inférieure au contenu recherché,
avancer d’une case. Si on épuise la liste ou qu’on tombe sur une valeur strictement supérieure à la valeur
recherchée, c’est qu’elle n’est pas dans la liste. Sinon, elle y est.
11. Deuxième édition, Franklin, Beedle & Associates
12. On dit parfois déverminage en français, mais personnellement, je trouve que ça supprime le côté affectif... ;-)
13. Rien n’est à l’épreuve des imbéciles, car ils sont trop malins.
34
Notes de cours, Ensip 1A
si i ≥longueur(l) ou l [ i ]>v :
retourner Faux
sinon
retourner vrai
Il ne nous reste plus qu’à traduire cet algorithme, par exemple en Python :
De la même idée de départ nous aurions pu décliner un algorithme formel un peu différent et finir sur un autre
programme tout aussi correct :
Algorithme de la méthode dichotomique Pour chercher v entre les cases g et d, regarder le contenu de la case
du milieu m (m = (g + d)/2). S’il est plus petit que v, continuer à chercher dans l’intervalle m, d sinon, continuer à
chercher dans l’intervalle g, m.
Voici l’algorithme, écrit de manière plus formelle :
Pour rechercher v dans la liste l :
g,d ←0,len ( l )−1
répéter tant que la «tranche» g,d fait plus d’une case :
m←milieu de g,d
si v>l[m] : g←m+1
14. la numérotation à partir de 0 est habituelle en informatique, même si elle n’est pas utilisée dans tous les langages.
35
Algorithmique et Programmation avec Python
sinon : d←m
si l [g]=v : retourner Vrai
sinon retourner Faux
Cette fonction a beau être courte et assez simple, elle peut contenir des erreurs qui ne sont pas évidentes à détecter.
Par exemple, si nous avions écrit :
alors le programme n’aurait pas fonctionné. Vous vous en rendrez compte en exécutant à la main ce que fait l’algorithme
pour rechercher la valeur 5 dans la liste [3,4].
Exécution manuelle
Exécuter un algorithme à la main, pour quelques jeux de valeur, est essentiel dans la détection des erreurs. Il faut
le faire de manière systématique, et ne pas s’en priver, à plus forte raison, lorsque l’algorithme est simple.
Notion de complexité
Pouvions nous prédire les performances de ces algorithmes sans les programmer, afin d’estimer leurs mérites respectifs
et choisir la bonne méthode ?
Dans le cas de la recherche naïve, le nombre d’opérations dans la première boucle est proportionnel à la position de
la valeur dans le tableau. La fin de l’algorithme s’exécute en temps constant. En moyenne, le nombre d’opérations est
donc k1 n2 + k2 . On dit que l’algorithme est en Θ(n) ou linéaire. Cela signifie que son temps d’exécution varie comme
n. Autrement dit, lorsque n est grand, si on multiplie la taille des données par k, le temps d’exécution sera lui aussi
multiplié par k.
15. En rouge si vous avez la couleur...
36
Notes de cours, Ensip 1A
1800
0.08 1600
1400
0.06 1200
1000
0.04 800
600
0.02 400
200
0 0
0 1 2 3 4 5 6 7 8 9 ×107 0 1 2 3 4 5 6 7 8 9 ×107
Figure II.2 – Comparaison des temps (s) d’exécution de deux algorithmes de recherche
Notation de Landau
Une fonction g(n) appartient Θ(f (n)) si :
Dans le cas de la recherche dichotomique, le corps de la boucle s’exécute en temps constant. La boucle s’exécute jusqu’à
ce que g et d soient égaux. La distance g − d est divisée par 2 à chaque tour. On a donc ln2 (n) tours de boucle. Le
nombre d’opérations est donc : k1 ln2 (n) + k2 . On dit que l’algorithme est en Θ(log(n)) ou logarithmique. Autrement
dit lorsque n est grand, pour doubler le temps d’exécution, il faut élever le nombre de cases au carré ! Cet algorithme
est donc incroyablement plus rapide que le précédent.
Est-ce vraiment important ?
Un algorithme logarithmique est considérablement plus efficace qu’un algorithme linéaire (lui-même considérable-
ment plus efficace qu’un algorithme quadratique, ou encore exponentiel).
Cette différence devient fondamentale si la taille des données est importante.
On peut estimer que pour un tableau de 1010 valeurs,
1. le temps moyen pour retrouver un seul nombre serait d’environ 2 heures dans le premier cas
2. il serait de moins d’une milliseconde dans l’autre cas
Calcul de complexité
Pour détermine le coût c(a) d’un algorithme a, on peut faire le décompte suivant :
— les opérations «atomiques» : affectation, comparaison, opérations arithmétiques ou composition de tout ça
(sans boucle) sont considérées comme ayant un temps d’exécution unitaire (ou tout au moins constant).
— Coût d’une séquence p; q : c(´p; qˇ) = c(p) + c(q)
— Coût du test if b: p else: q : borné par c = c(b) + max(c(p), c(q))
— Coût d’une boucle for i in it: p
— c=n P× c(p) avec n = len(it) si c(p) ne dépend pas de i
— c = i∈it c(pi ) sinon
37
Algorithmique et Programmation avec Python
— Coût d’une boucle while b: p somme des c(b)+c(p) à chaque tour de boucle (on peut essayer de majorer....)
-2 9 -5 7 3 6
Nous devons écrire un algorithme capable de fournir en retour une liste triée par ordre croissant :
-5 -2 3 6 7 9
retourner min
Cet algorithme est quadratique (complexité en Θ(n2 )).
Tri rapide
Voici une autre idée d’algorithme (probablement moins facile à avoir) :
1. Sélectionner une valeur au hasard dans le tableau : le pivot
2. Organiser le tableau en deux parties : à gauche les éléments inférieurs au pivot et à droite, les éléments supérieurs
au pivot.
3. Trier les deux parties avec la même méthode
38
Notes de cours, Ensip 1A
Partitionner le tableau
Il existe plusieurs façons de partitionner le tableau, qui sont plus ou moins équivalentes, mais l’idée reste la même :
mettre les petits éléments à gauche, et les grands à droite.
échanger t [ f ] et t [ i + 1]
retourner i + 1
— La fonction partitionner a un temps d’exécution proportionnel à la taille de la tranche de tableau.
— La fonction tri_rapide est récursive. Son temps d’exécution est donné par une relation de récurrence :
partitionner
z}|{
T (n) = a n +2 × T (n/2)
| {z }
puisqu’en moyenne, p est
au milieu de la tranche
Tous calculs faits, on trouve une complexité en Θ(n log(n))
Comme pour le cas de la recherche, connaître la complexité des algorithmes est une indication particulièrement utile
dès que l’on a beaucoup de grandes quantités de données. La figure II.3 montre les temps mis pour trier des tableaux
de 500 à 10 millions de valeurs, en utilisant 3 algorithmes différents : le tri par fusion, le tri par sélection et le tri
rapide.
Temps d’éxecution moyen en millisecondes de trois algorithmes de tri
en fonction du nombre d’entiers à trier
200
Temps (ms)
Fusion
180 Sélection
Rapide
k.n.ln (n)
160
k.n 2
140
120
100
80
60
40
20
0
0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 2
Nb d’entiers 5
x 10
39
Algorithmique et Programmation avec Python
17 Récursivité et Logo
Le monde tel qu’il est vu par Logo, un langage à but pédagogique inventé dans les années 60 par Seymour Papert, est
un espace en deux dimensions 16 dans lequel se déplace une tortue, qui peut ou non laisser une trace des endroits où
elle passe. Un programme Logo est composé d’ordres donnés à la tortue (comme «avance de 10» ou «tourne à gauche
de 90 degrés»).
Nous allons ici utiliser le module turtle de Python, qui permet de programmer comme en Logo. Les programmes
pouvant être récursifs, c’est l’aspect graphique du Logo et la possibilité d’utiliser la récursivité qui permet de réaliser
des figures complexes en très peu de lignes.
def carre(cote):
for i in range(4):
fd(cote)
rt(90)
def fig(l):
fd(l)
lt(60)
fd(l)
rt(120)
fd(l)
lt(60)
fd(l)
40
Notes de cours, Ensip 1A
Comment tracer de telles figures ? La réponse est très simple, et correspond tout à fait à la façon de tracer la figure à
la main. Il suffit de remplacer chaque segment par la figure elle-même, trois fois plus petite, comme dans le programme
suivant :
Ce programme comporte néanmoins une grosse erreur. Lorsque longueur vaudra par exemple 1, la tortue va continuer
la descente récursive et vouloir tracer la figure pour une arête de 31 , puis 19 , ... sans jamais s’arrêter... Nous devons
donc, de même que nous arrêtons la descente récursive lors d’un dessin à la main, indiquer à la tortue à quel moment
elle ne doit plus remplacer un segment potentiel par la figure complète, mais bien tracer le segment. Nous pouvons
par exemple décider que lorsque la longueur sera inférieure ou égale à un, alors, il faudra arrêter la descente récursive.
À ce moment, au lieu de tracer le dessin de la figure II.5(a), il faudra faire un simple segment comme indiqué
figure II.5(b).
r/3
lo n
eu
gu
gu
eu
lo n
r/3
def fig(l):
if l < 2 :
fd(l)
return
fig(l / 3)
lt(60)
fig(l / 3)
rt(120)
fig(l / 3)
lt(60)
fig(l / 3)
Triangle de Sierpinski Le triangle de Sierpinski est obtenu par les opérations suivantes. On dessine tout d’abord
un triangle (figure II.6(a)), puis, on divise en 4 triangles (figure II.6(b)). Chaque triangle extérieur (il y en a 3) est
considéré comme le triangle de départ. Il est donc divisé en 4 (figure II.6(c)). Au bout de quelques itérations, on obtient
la figure II.6(d).
Courbe du dragon La récursivité est parfois «croisée» en ce sens que ce n’est pas toujours la procédure A qui
s’appelle elle-même. Ce peut être la procédure A qui appelle la procédure B, et la procédure B qui appelle la procédure
A. C’est par exemple le cas dans la courbe du dragon. Sur la figure II.7. sont représentées les courbes :
1. dragon droit de profondeur 1
41
Algorithmique et Programmation avec Python
(d)
1 2 3 4 5 6 7 8
Vous devriez remarquer (mais ce n’est pas facile) qu’un dragon gauche de profondeur n est composé d’un dragon
gauche de profondeur n − 1 d’une rotation de + π2 , et d’un dragon droit de profondeur n − 1. Inversement un dragon
droit de profondeur n est composé d’un d’un dragon gauche de profondeur n − 1 d’une rotation de − π2 , et d’un dragon
droit de profondeur n − 1. Enfin, un dragon de profondeur 0 est simplement un trait.
Si l’on réduit la taille des traits à 1, on obtient comme dragon gauche de profondeur 15 l’objet de la figure II.8.
42
Notes de cours, Ensip 1A
43
Algorithmique et Programmation avec Python
44
Chapitre III
Dans ce chapitre sont regroupées des informations sur l’utilisation de Python. Elles ne sont pas exhaustives (le lecteur
est encouragé à consulter la documentation officielle), mais pourront aider à débloquer rapidement bon nombre de
situations.
18 Types simples
18.1 Entiers
Le type int, permet de représenter les entiers de taille machine (quelques octets) ou les grands entiers (limités
uniquement par la taille de la mémoire). Le passage de l’un à l’autre (entiers machines ou grands entiers) est transparent
pour l’utilisateur depuis la version 3 de Python.
Pour indiquer une valeur entière, il est possible d’utiliser différentes bases :
>>> 42 # en décimal
45
Algorithmique et Programmation avec Python
Les opérations disponibles sur les nombre entiers sont résumées dans la table III.1.
Les opérations de conversion vers les différentes bases sont réalisés avec les fonctions bin, oct, et hex.
Les opérations arithmétiques ordinaires sont disponibles sur les entiers :
>>> 6 + 9
15
>>> 6 - 9
-3
>>> 6 * 9
54
>>> 9 / 6
1.5
>>> 9 // 6 # division entière
1
>>> 9 % 6 # reste
3
Division entière
Notons qu’en Python 3, l’opérateur de division / est un opérateur de division non entière contrairement à ce qui se
faisait en Python 2 et contrairement ce qui se fait dans de nombreux langages. Même si une division tombe juste,
le résultat de l’opération / sera un float. Il faut donc penser, lorsqu’on souhaite manipuler des entiers, à utiliser
l’opérateur //.
18.2 Booléens
Les deux seules valeur du type bool sont True et False. Ces deux valeurs peuvent être entrées litéralement ou peuvent
être le résultat d’opérateurs de comparaison, comme >, <, >=, <=, == (égal), != (différent).
Les opérateurs dont les opérandes sont des booléens sont les opérateurs logiques or, and, not.
46
Notes de cours, Ensip 1A
Les expression qui ne sont ni le résultat de connecteurs logiques, ni le résultat d’opérateurs de comparaison sont
évaluées à True sauf : False, None, la valeur 0 ou 0.0, les collections vides (tuples, dictionnaires, listes, ensembles).
>>> a = 7.54
>>> b = 7e3
>>> c = 0.745e1
>>> a = 4 + 3j
>>> b = 5 + 0j
>>> a = complex(4, 3)
>>> b = complex(5, 0)
Ne pas confondre le j complexe et la variable j. Ce qui suit ne crée pas de nombre complexe :
>>> a = 5 + 1j
>>> b = 5 + 3j
Voici quelques méthodes et accesseurs sur les nombres complexes (le module cmath contient d’autres outils) :
47
Algorithmique et Programmation avec Python
>>> a = 5 + 4j
>>> a.real
5.0
>>> a.imag
4.0
>>> abs(a)
6.4031242374328485
19 Types collections
Les collections : listes, tuples, objets itérables etc. font la force et l’efficacité des langages interprétés modernes. Savoir
les manipuler permet de concevoir des programmes concis, élégants et efficaces.
Conteneur ou Collection objet destiné à contenir d’autres objets. Une collection peut être ordonnée (c’est alors
une séquence) ou non. Les conteneurs standards sont : list tuple set str dict...
Séquence collection ordonnée d’éléments indicés par des entiers. Les séquences Python sont par exemple : list,tuple,str.
On peut accéder à un élément d’une séquence en précisant entre crochets le numéro d’ordre de l’enregistrement.
La numérotation commence à 0. (un objet de type range se comporte aussi comme une séquence)
Modifiable capacité pour un objet (pas forcément une collection) de modifier son contenu sans que l’objet soit
recréé. Ce point est délicat, et il est assez spécifique à Python. Dans le cas d’un conteneur, on peut le quali-
fier de récursivement (ou complètement) non modifiable s’il est non modifiable et que tous les éléments qu’il
contient sont récursivement non modifiables. Les termes anglais pour modifiable/non modifiable sont : mutable,
immutable.
Type list séquence modifiable d’éléments éventuellement hétérogènes
Type tuple séquence non modifiable d’éléments éventuellement hétérogènes.
Hashable Un objet hashable est un objet récursivement non modifiable. Il peut servir de clé dans un dictionnaire.
Les éléments d’un ensemble (set) doivent être hashables.
Type dict (dictionnaire ou tableau associatif) : collection non ordonnée modifiable d’éléments éventuellement
hétérogènes. Un tableau associatif est un type de données permettant de stocker des couples clé/valeur, avec
un accès rapide à la valeur à partir de la clé. Celle-ci ne peut bien sûr être présente qu’une seule fois dans le
tableau. La clé doit être hashable. Le type dict fait partie des collections de la catégorie Mapping qui associent
un objet à un autre.
Type set collection non ordonnée d’éléments hashables distincts.
Itérable capacité, pour une collection, d’égrener ses valeurs, par exemple avec une boucle for Python. Les objets
itérables peuvent être parcourus, mais on ne peut pas nécessairement prendre un élément ou un autre en
l’adressant.
La table III.2 donne les propriétés des différents types collection.
48
Notes de cours, Ensip 1A
[1, 3, 5, 7] 3
>>> t=(2, 4, 6, 8) # création d’un tuple
>>> print(t, t[2])
(2, 4, 6, 8) 6
>>> s=’Supercalifragilistique’ # création d’une chaîne
>>> print(s, s[4])
Supercalifragilistique r
Indices négatifs
En Python, il existe un moyen de désigner les éléments d’une séquence en partant de la fin : s[-1] est le dernier
élément de la séquence, s[-2] est l’avant-dernier etc... En règle générale, si k>0, s[-k] vaut s[len(s)-k].
Pour les dictionnaires, les numéros sont remplacés par des clés (n’importe quel élément hashable) :
2. L’opération d’extraction d’une tranche de tableau se nomme slicing en anglais. En Python, elle crée un nouvel objet, et non une vue
sur l’objet d’origine.
49
Algorithmique et Programmation avec Python
Mais c’est une copie superficielle (shallow copy). Si un des éléments est modifiable (si c’est une liste par exemple), ça
peut être un problème. Le test suivant permet de comprendre ce qui se passe :
Pour obtenir un comportement différent, on dispose d’un module copy qui offre une copie en profondeur (deep copy) :
50
Notes de cours, Ensip 1A
51
Algorithmique et Programmation avec Python
Méthode Description
x + y Concaténation
x * i Retourne un nouvel itérable contenant i copies de x
e in x Retourne True si e est dans x et False sinon
all(x) Vaut True si chaque élément de x est évalué à True
any(x) Vaut True s’il y a au moins un élément de x évalué à True
enumerate(x,start) Retourne une séquence de tuples de la forme (index,item) énumérant les éléments
de x avec leur position. start est un offset appliqué à la position initiale (0).
len(x) Nombre d’éléments de x
max(x, key) Retourne le plus grand élémént de x. L’argument key a le même rôle que pour la
fonction sorted
min(x, key) Retourne le plus petitélémént de x. L’argument key a le même rôle que pour la
fonction sorted()
reversed(x) Retourne un nouvel itérateur contenant les même éléments que x mais en sens
inverse.
sorted(x, key, reverse) Retourne une liste contenant les mêmes éléments que x, mais triés en utilisant la
méthode de comparaison __cmp__ de ses éléments. Si reverse vaut True, le tri
est fait à l’envers (décroissant). Le paramètre key est une fonction qui prend en
paramètre un élément et renvoie la valeur qui sera utilisée réellement pour le tri.
sum(x, start) Retourne la somme des éléments de x augmentée de start (qui vaut 0 par défaut)
zip(x1, ..., xN) Retourne un nouvel itérateur contenant des tuples de N élément. Chaque élément est
pris dans un des itérateurs. Exemple : zip((’a’, ’b’, ’c’),(1, 2, 3)) vaudra
((’a’, 1), (’b’, 2), (’c’, 3))
Méthode Description
dict() Retourne un dictionnaire
dict(mapping) Construit un dictionnaire à partir d’un objet de type mapping (clé/valeur)
dict(seq) Construit un dictionnaire à partit d’une séquence. Remplace le code D = {}; ←֓
for k, v in seq: D[k] = v
dict(**kwargs) Construit un dictionnaire à partir de paires nom=valeur :
dict(alpha=1,beta="omega")
k in D Vaut True si k est une clé de D et faux sinon
del D[k] Efface la clé k du dictionnaire
D1 == D2 Retourne True si les dictionaires ont les même clés associées aux mêmes valeurs
(comparaison avec ==)
D[k] Renvoie la valeur associée à la clé k dans D. Si la clé k n’existe pas,lève l’exception
KeyError
iter(D) Renvoie un itérateur sur le dictionnaire
len(D) Renvoie le nombre de clés de D
D1 != D2 Vaut Vrai si D1 et D2 ont des clés différentes ou que certaines clés identiques sont
associées à des valeurs différentes
repr(D) Retourne la représentation de D
D[k] = e Associe la valeur e à la clé k
D.clear() Vide D de ses clés
D.copy() Retourne une copie non récursive de D
D.get(k[, e]) Retourne D[k] si k est une clé de D et e sinon (par défaut, e vaut None
D.items() Retourne une vue sur le dictionnaire. Souvent utilisé sous la forme : for k,v ←֓
in D.items():...
52
Notes de cours, Ensip 1A
Méthode Description
set(iter) Retourne un ensemble, contenant les éléments de l’itérable
k in E Vaut True si k est dans l’ensemble et faux sinon
E.pop() Efface un élément au hasard et le renvoie
E | F Ensemble union de deux ensembles
E ^ F Ensemble des éléments qui sont dans E ou dans F, mais pas dans E et F (différence
symétrique)
E & F Ensemble intersection de E et F
E - F Ensemble des éléments qui sont dans E mais pas dans F
E <= F Vaut True si E est un sous ensemble de F
53
Algorithmique et Programmation avec Python
Un objet est modifiable si on peut modifier le contenu référencé. Le contenu d’une liste étant une liste de références,
et une liste étant modifiable, on peut changer ces références, ce qui autorise à écrire :
>>> l=[1, 2, 3]
>>> id(l[0])
9357408
>>> l[0] = 42
>>> id(l[0]) # l’objet référencé n’est plus le même
9358720
>>> l
[42, 2, 3]
Un tuple n’étant pas modifiable, les mêmes opérations ne sont pas possible :
>>> t=(1, 2, 3)
>>> id(t[0])
9357408
>>> t[0] = 42 # on ne peut pas référencer un autre objet
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: ’tuple’ object does not support item assignment
En revanche, si un des éléments d’un tuple est une liste, et qu’on modifie cette liste, alors ça fonctionne... :
Une chaîne de caractères n’étant pas modifiable, on ne peut donc pas modifier un de ses caractères :
>>> s = ’Pithon’
>>> s[1] = ’y’
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: ’str’ object does not support item assignment
>>> s = ’Pithon’
>>> s = s[:1] + ’y’ + s[2:]
>>> s
’Python’
ou bien :
54
Notes de cours, Ensip 1A
>>> s = ’Pithon’
>>> ls = list(s)
>>> ls
[’P’, ’i’, ’t’, ’h’, ’o’, ’n’]
>>> ls[1] = ’y’
>>> s=’’.join(ls)
>>> s
’Python’
21 Chaînes de caractères
Le type correspondant aux chaînes de caractères est str. Une chaîne de caractères est une séquence non modifiables,
composée de caractères unicodes (voir 2.4).
>>> s1 = ’Python’
>>> s2 = ’ et les chaînes’
>>> len(s1)
6
>>> s1 + s2
’Python et les chaînes’
>>> len(s1 + s2)
21
On peut employer des caractères spéciaux dans les chaînes. Ces caractères sont généralement échappés par \. En
particulier, si la chaîne est délimitée par des guillemets simples, il faudra échapper les guillemets simples qu’elle
contient.
55
Algorithmique et Programmation avec Python
Une chaîne étant une séquence, il est possible d’accéder à ses éléments (les caractères) avec l’opérateur [.]. En outre,
puisque le type chaîne n’est pas mutable, on ne peut pas modifier un caractère dans une chaîne, il faut en recréer une :
>>> s = ’Pithon’
>>> s[1]
’i’
>>> s[1] = ’y’
TypeError: ’str’ object does not support item assignment
>>> s[:1] + ’y’ + s[2:] # création d’une nouvelle chaîne
’Python’
Il est possible d’obtenir le code d’un caractère ou le caractère correspondant à un code particulier avec les fonctions
ord et chr :
>>> ord(’A’)
65
>>> chr(65)
’A’
>>> chr(216) + chr(169)
c
’Ø
’
La fonction input renvoie une chaîne de caractères, mais on peut la convertir au passage :
>>> a = int(input(’Entrez votre âge : ’))
Entrez votre âge : 3
>>> print(a, type(a))
3 <class ’int’>
56
Notes de cours, Ensip 1A
>>> a = 43.34456
>>> b = 12
>>> print("Un entier {} et un float {}".format(b, a))
Un entier 12 et un float 43.34456
>>> print("Un entier {0} et un float {1}".format(b, a))
Un entier 12 et un float 43.34456
>>> print("Un entier {1} et un float {0}".format(a, b))
Un entier 12 et un float 43.34456
>>> print("Un entier {0:03d} et un float {1:.2f}".format(b, a))
Un entier 012 et un float 43.34
57