Il existe des façon plus astucieuses de réaliser les boucles dans certains cas.
Par exemple, pour l'exercice 2 : afficher les entiers de 10 à 21, on pouvais écrire :
for i in range(start,stop) : la boucle début avec i=start et stoppe avec i=stop-1 mais : for i in range(10,4) : commençant à 10 et finissant à 3, cette boucle ne sera pas exécutée !
Cependant... il est possible de faire aussi des boucles décroissantes. A l'exercice 4 on demandait d'afficher les entiers 97, 91, 85, 79. Cela est réalisé par :
for i in range(start,stop,pas) : la boucle débute avec i=start i augmente de pas à chaque itération. On arrete quand on atteint ou dépasse stop. ainsi: for i in range(10,14,3) : commençant à 10, puis 13 et on stoppe là, la valeur suivante serait 16 dépasse 13 for i in range(10,4,-3) : commençant à 10, puis 7 et on stoppe là, la valeur suivante serait 4 atteint la limite 4
La table de 9
C'est assez ennuyeux de devoir répéter 10 fois quasiment la même ligne...
Complète le code ci-dessous qui fait la même chose avec une boucle
Si vous demandez à Python d'utiliser une variable qui n'est pas définie, vous obtenez une erreur.
NameError
Comme vous pouvez le constater, nous obtenons un message d'erreur disant NameError: name 'trouble' is not defined. Quelquefois, de telles erreurs sont dues à des erreurs de frappes : si vous définissez une variable adresse=32, et ensuite print(adrese), le même type d'erreur est généré.
SyntaxError
Un autre type d'erreur fréquent est généré quand vous écrivez une instruction qui n'a pas de sens pour python.
Par exemple, si vous inversez le sens de l'instruction =.
La première ligne est bien mais la deuxième génère une erreur : Python pense que la deuxième ligne 4 = x essaye de changer la valeur de 4 mais vous n'êtes autorisé qu'à changer la valeur d'une variable, et 4 n'est pas une variable. Si A = B et B = A représentent la même chose en mathématiques, ce n'est pas le cas en programmation.
Autre exemple, si vous omettez quelques chose, par exemple, une parenthèse :
L'instruction print(x) est incomplète, il manque la parenthèse fermante. L'interpréteur Python cherche la parenthèse manquante et constate l'erreur seulement quand il passe à la ligne suivante. Pour cette raison, il indique l'erreur à la ligne juste en dessous et non pas à l'endroit ou la parenthèse manque.
IndentationError
On s'attendrait que le programme affiche 5, mais il génère une erreur. Examinons-la. Il faudra d'ailleurs prendre l'habitude de décrypter les messages d'erreur pour pouvoir comprendre l'erreur commise. Corrigez l'erreur pour que le code s'exécute correctement.
TypeError
On s'attendrait que le programme affiche 0.5, mais il génère une erreur.
Littéralement : Erreur de type : les types des opérandes pour la division ne sont pas supportés : str et int.
En clair : vous effectuez la division d'une chaine par un entier, cela n'a pas de sens et n'est pas supporté par l'interpréteur.
Exercices
Ceci est un échauffement pour vous familiariser avec les variables.
Anatomie Python
Désordre
programme d'échange
Voici un tout premier algorithme, qui est très fréquemment utilisé. Le problème est d'échanger les contenus de deux variables, nommons les x et y.
Par exemple, initialement on a :
x = 4
y = 3
Et on souhaite modifier l'état pour parvenir à :
x = 3
y = 4
On pourrait songer à écrire que x prend la valeur de y et y prend la valeur de x. C'est ce que fait le code ci-dessous, mais vous allez pouvoir constater que celà ne va pas fonctionner....
Nous allons voir comment programmer cela. L'idée est donc que la fonction sum_inverse_carres s'appelle elle même. On désigne cela par le terme de fonction récursive.
Toutefois, il faut que le processus s'arrête.... Ici on appelle la fonction avec n-1 donc la valeur de l'argument décroit. Jusqu'où ???
En fait, dans tout programmation récursive on doit définir un cas de base, un cas simple où le résultat est trivial, et on peut renvoyer ce résultat sans rappeler la fonction.
Ici le cas de base est n = 1, et la valeur renvoyée dans ce cas est 1.
Exercices
Exercice 1 : somme des entiers de 0 à n
Exercice 2 :
Oui... je sais, nous l'avons déjà vu en exemple .... mais saurez vous le refaire ?
Il faut toujours penser à vérifier les cas "limites".
Votre fonction renvoie-t-elle le bon résultat pour ?
Une manière originale et très puissante de générer des listes est d'utiliser une liste en compréhension (ou la compréhension de listes). Pour plus de détails, consultez à ce sujet le site de Python et celui de Wikipédia.
Voici quelques exemples.
Nombres pairs compris entre 0 et 30
Jeu sur la casse des mots d'une phrase
Formatage d'une séquence avec 60 caractères par ligne
Exemple d'une séquence constituée de 150 alanines :
Cette section est particulièrement importante et doit être bien comprise
Vous voyez que la modification de x modifie y aussi ! Dans python tutor ( cliquer sur visualiser pour acceder à python tutor) vous pouvez voir que x=y n'a pas dupliqué la liste.
Techniquement, Python utilise des pointeurs (comme dans le langage de programmation C) vers les mêmes objets. Python Tutor l'illustre avec des flèches qui partent des variables x et y et qui pointent vers la même liste. Donc, si on modifie la liste x, la liste y est modifiée de la même manière. Rappelez-vous de ceci dans vos futurs programmes car cela pourrait avoir des effets désastreux !
Pour éviter ce problème, il va falloir créer une copie explicite de la liste initiale. Observez cet exemple :
Si on regarde à nouveau dans Python Tutor (Figure 12), on voit clairement que l'utilisation d'une tranche [:] ou de la fonction list() crée des copies explicites. Chaque flèche pointe vers une liste différente, indépendante des autres
Attention, les deux techniques précédentes ne fonctionnent que pour les listes à une dimension, autrement dit les listes qui ne contiennent pas elles-mêmes d'autres listes.
fonction algo1(tableau) : pour i allant de 0 à longueur(tableau)-2 : imin=i pour j allant de i+1 à len(tableau)-1 : si tableau[j] < tableau[imin] : imin=j fin de si fin de pour if imin!=i: tableau[i],tableau[imin]=tableau[imin],tableau[i] fin de si renvoyer tableau
Exercice à choix multiple
L'algorithme ci-dessus correspond à ...
Correct !
Deuxième algo
fonction algo2(lst) :
pour i de 1 à len(lst) - 1
# mémoriser lst[i] dans x
x ← lst[i]
# décaler vers la droite les éléments de lst[0]..lst[i-1] qui sont plus grands que x en partant de lst[i-1]
j ← i
tant que j > 0 et lst[j - 1] > x
lst[j] ← lst[j - 1]
j ← j - 1
fin de tant que
# placer x dans le "trou" laissé par le décalage
lst[j] ← x
renvoyer lst
Exercice à choix multiple
L'algorithme ci-dessus correspond à ...
Correct !
Comment interpréter les nombres de comparaison des algorithmes 2 et 3 ?
Pouvez vous donnez le nombre de comparaisons de l'algo 1 pour une liste de 200 valeurs ?
Pour l'algo2, que pourrait on dire ?
Et quelle est la complexité de l'algo 3 ?
Troisième algo
fonction algo3(x,lst) :
imin=0
imax=len(lst)-1
imilieu=(imin+imax)//2
tant que imin <= imax :
si lst[imilieu]>x
# on va chercher dans la partie de gauche :
imax=imilieu-1
sinon si lst[imilieu] < x :
# on va chercher dans la partie de droite:
imin=imilieu+1
sinon :
# on a trouvé x
renvoyer True
fin de si
imilieu=(imin+imax)//2
fin de tant que
renvoyer False
Exercice à choix multiple
L'algorithme ci-dessus correspond à ...
Correct !
S'il vous reste du temps...
Modulo 7
Voici un graphe (nous étudierons les graphes plus en détail bientôt) qui permet de calculer N%7 pour tout nombre entier N :
Ce graphe contient des flèches noires et des flèches bleues. Votre premier travail est de modéliser le graphe avec deux dictionnaires : BLACK et BLUE.
Dans chacun des dictionnaires, les clés sont les valeurs de départ des flèches (noires ou blueues) et les valeurs sont les valeurs d'arrivée.
Par exemple, dans le dictionnaire NOIR, la clé 0 à pour valeur 1.
Ensuite, vous devez implementer l'algorithme de détermination de N%7. Etudions un exemple :
Nous avons utilisé les listes très fréquemment et nous allons rapidement revoir les principales méthodes associées aux objets de type list.
Comme pour les chaînes de caractères, les listes possèdent de nombreuses méthodes qui leur sont propres et qui peuvent se révéler très pratiques. On rappelle qu'une méthode est une fonction qui agit sur l'objet auquel elle est attachée par un point. Nous étudierons la programmation orientée objet (POO) très prochainement.
AJOUTER : .append et .insert
La méthode .append(), ajoute un élément à la fin d'une liste tandis que La méthode .insert() insère un objet dans une liste avec un indice déterminé :
LOCALISER, COMPTER : .index et .count
La méthode .count() compte le nombre d'éléments (passés en argument) dans une liste : La méthode.index()donne la position d'un élément dans une liste.
TRIER : .sort et .sorted et aussi reverse
La méthode .sort() trie une liste en laissant les valeurs triées dans cette liste tandis que .sorted() renvoie une copie de la liste triée
SUPPRIMER : del remove pop
Construction d'une liste par itération
La méthode .append() est très pratique car on peut l'utiliser pour construire une liste au fur et à mesure des itérations d'une boucle.
Pour cela, il est commode de définir préalablement une liste vide de la forme maliste = []. Voici un exemple où une chaîne de caractères est convertie en liste :
La méthode avec list(seq) est certes plus simple, mais il arrive parfois qu'on doive utiliser des boucles tout de même, comme lorsqu'on lit un fichier. On rappelle que l'instruction list(seq) convertit un objet de type chaîne de caractères en un objet de type liste. De même que list(range(10)) convertit un objet de type range en un objet de type list ou encore list(dic.keys()) converti la liste des clés d'un dictionnaire en un objet de type liste.
Appartient : in
Autres méthodes utiles
Le type liste de python dispose de méthodes supplémentaires par rapport à ce qui précède. Voici toutes les méthodes des objets de type liste :
list.extend(iterable) Étend la liste en y ajoutant tous les éléments de l'itérable. Équivalent à a[len(a):] = iterable.
list.clear() Supprime tous les éléments de la liste. Équivalent à del a[:].
list.index(x[, start[, end]])
Renvoie la position du premier élément de la liste dont la valeur égale x (en commençant à compter les positions à partir de zéro). Une exception ValueError est levée si aucun élément n'est trouvé. Les arguments optionnels start et end sont interprétés de la même manière que dans la notation des tranches et sont utilisés pour limiter la recherche à une sous-séquence particulière. L'indice renvoyé est calculé relativement au début de la séquence complète et non relativement à start.
Dans cette partie nous utiliserons donc ce jeu de primitives :
listeVide (): renvoie une LISTE vide estListeVide (l): renvoie True si l est une LISTE vide, False sinon cons(x,l) : renvoie une nouvelle LISTE égale à l plus l'élément x ajouté en tête listeQueue(l) : renvoie la queue de l listeTete(l) : renvoie la tête de la LISTE
et nous ajouterons une première fonction dérivée des primitives de base : longueur(l) : renvoie la longueur de la LISTE l
Le type des objet ainsi créés est nommé LISTE
une première implémentation
Complexité
Exercice à choix multiple : Complexité de la fonction cons :
le nombre d'instructions requis pour cette fonction est :
Correct !
Exercice à choix multiple : Complexité de la fonction longueur :
le nombre d'instructions requis pour cette fonction est :
Correct !
Une seconde implémentation
Bon, il est possible que notre représentation avec les tuples imbriqués ne vous plaise pas beaucoup... non ? En fait ceci n'est qu'une affaire de notation, et n'a pas grande importance. Dans l'exemple ci-dessous, nous allons modifier le constructeur pour changer la représentation de nos LISTE mais nous ne changeons pas les autres primitives (sauf listeQueue de façon marginale), et pas plus la fonction dérivée longueur, et tout fonctionne de façon identique :
Notez bien que, malgré l'implémentation un peu différente, les appels aux primitives sont strictement identiques (le programme principal est strictement identique, la fonction longueur également).
La représentation est bien la même, nous avons seulement affecté la notation. Dans la suite, nous utiliserons cette version, qui donne un affichage plus lisible, mais pour autant, ne perdez pas de vue que nos LISTE sont toujours une tête et une queue.
Comme les listes, les piles, et les files, il est important de distinguer le type abstrait de représentation des données, du type implémenté dans le langage.
En Python il n'existe pas de type GRAPHE prédéfini. C'est donc à chacun de l'implémenter avec les outils disponibles dans le langage.
Comme pour les listes il peut exister, selon les auteurs, des différences sur le nombre et la définition des primitives de bases. Nous allons utiliser ici ce jeu de primitives :
creerGrapheVide() : retourne un graphe vide
estVideGraphe(G) : retourne True si G est un graphe vide
ajouterSommet(G,s) : retourne le graphe après ajout du sommet s
supprimerSommet(G,s) : retourne le graphe après suppression du sommet s (et tout les arcs rliés à s)
existeSommet(G,s) : retourne True si s est un sommet du graphe G
Pour un graphe orienté :
ajouterArc(G,sd,sa) : retourne le graphe après ajout d'un arc reliant sd à sa (orienté)
supprimerArc(G,sd,sa) : retourne le graphe après suppression de l'arc sd->sa
existeArc(G,sd,sa) : retourne True si sd->sa est un arc du graphe G
Pour un graphe non orienté :
ajouterArete(G,sd,sa) : retourne le graphe après ajout d'une arête reliant sd à sa (non-orienté)
supprimerArete(G,sd,sa) : retourne le graphe après suppression de l'arête sd->sa
existeArete(G,sd,sa) : retourne True si sd->sa est une arête du graphe G
Une première implémentation (d'un graphe orienté) avec un dictionnaire
Comme nous l'avons vu, un graphe peut être représenté par une liste de successeur. Cette représentation peut être implémentée efficacement avec un dictionnaire. Chaque sommet sera donc une clé dictionnaire, et sa valeur sera la liste des sommets sa reliés à s dans le sens s -> sa.
En conséquence, un graphe vide est un dictionnaire vide.
Une solution serait d'initialiser une variable avant la boucle...
Une solution serait d'initialiser une variable avant la boucle... et dans la boucle, changer la valeur de façon adéquate
La première ligne de votre programme pourrait être
tetes = 1 * personnes
comme le nombre de têtes est égal au nombre de personnes.
Notez que le programme définit quatre nouvelles variables. Assurez-vous que les lignes soit dans l'ordre afin que chaque nouvelle variable soit définie avant d'être utilisée.
Vous n'avez pas besoin d'utiliser d'arithmétique (+-*/) pour résoudre ce problème. Vous avez seulement besoin des variables et de l'instruction =. Vous pouvez définir de nouvelles variables avec des noms à choix si vous en avez besoin.
Commencez par comprendre ce qui se passe lorsqu'on exécute le code fournit au départ. Pourquoi le résultat erroné que l'on observe se produit-il ?
Il y a un problème. Par exemple, disons que le correcteur commence par définir x égal à 10 et y égal à 99. Si on exécute le programme, après la ligne x = y, on change la valeur de x (et pas celle de y) qui devient 99, et on a donc à la fois x et y qui sont égaux à 99.
La seconde ligne n'a alors pas d'effet. Comment peut-on garder la valeur originale de x quelque part pour pouvoir la mettre dans y plus tard ?
Vous pourriez ajouter une ligne comme xOriginal = x, pour stocker la valeur originale de x. Ensuite, vous pouvez attribuer x = y. Et enfin, attribuer à y la valeur original de x.