{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Boucles (FOR, WHILE)\n", "___\n", "\n", "Pré-requis : vous avez terminé la page du notebook sur le IF.\n", "\n", "Références de cette section :\n", " - [Le cours Moodle sur les boucles](https://moodle.insa-toulouse.fr/course/view.php?id=1090#section-3)\n", " - [The Python Language Reference -- Compound statements](https://docs.python.org/3/reference/compound_stmts.html)\n", " - [The Python Tutorial -- Control flow](https://docs.python.org/3/tutorial/controlflow.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## FOR : itérer sur une séquence\n", "\n", "Dans les différents langages de programmation, on peut distinguer au moins trois sortes de ```for``` :\n", " - le for sur un intervalle entier, comme en Ada : ```for x in 1..10 loop```\n", " - le for qui n'est qu'un while déguisé, comme en C : ```for (bloc;cond;bloc)```\n", " - le for qui est un **itérateur** sur une structure, que l'on appelle parfois *for-each* pour le distinguer des deux cas précédents.\n", "\n", "En Python, le ```for``` est un foreach : il parcourt une séquence de valeurs." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for x in (10,20,10,20,10,20,10,20,19):\n", " print(x)\n", " \n", "## CTRL+ENTREE ... ESC+o" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Séquences énumérables\n", "\n", "Toute structure énumérable convient dans un for. Testez le code ci-dessous avec :\n", "\n", " - Une paire ```(-5, -10)``` => les deux éléments sont énumérés.\n", " - Une chaîne de caractères ```\"2MIC\"``` => les caractères sont énumérés un par un.\n", " - Une liste ```[True, False, False]```\n", " - Un dictionnaire ```{ \"A\" : 1, \"B\" : 2, \"C\" : 3, \"D\" : 4 }```\n", " \n", " Vous constatez que l'ordre de l'énumération du dictionnaire n'est pas forcément l'ordre d'écriture. (Un dictionnaire est un ensemble de paires, ce n'est pas une séquence ordonnée).\n", " \n", " \n", " - En bon matheux, vous testez ce qui se passe avec un tuple vide ```()```, une liste vide ```[]```, ou une chaîne vide.\n", " - En bon informaticien, vous testez ce qui se passe avec quelque chose qui n'est pas énumérable : un booléen, un entier.\n", " \n", "### Range\n", " - Comme on a parfois besoin d'énumérer un intervalle entier [0;40], à la manière d'un FOR en Ada, il existe un constructeur de séquence ```range```\n", " \n", " - Testez le code ci-dessous avec ```range(10)```, ```range(10,20)```, ```range(20,10,-2)``` et vous aurez tout compris.\n", " \n", " - Au fait, combien d'éléments y'a-t-il dans ```range(10,20)``` ? Ne comptez pas sur vos doigts.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%reset -f\n", "\n", "## À vous de tester avec différentes constructions.\n", "sequence = [ 20 , 30 ]\n", "\n", "for elt in sequence:\n", " print(elt)\n", " \n", "print(\"\\nTest terminé.\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quelques questions :\n", " - Quel est le symbole que M. Le Botlan oublie à chaque fois qu'il écrit un FOR ?\n", " - Un FOR peut-il énumérer plusieurs fois la même valeur, ou chaque valeur n'apparait-elle qu'une seule fois dans l'énumération ?\n", " - À votre avis, est-ce une bonne idée, une mauvaise idée, ou sans importance, de modifier la structure sur laquelle le FOR itère ? Par exemple enlever ou ajouter des éléments à la liste que l'on parcourt ?\n", " - En Ada ou en C, si l'on veut parcourir un tableau, on écrit ```for No in Tab'Range``` (en Ada) ou ```for (int x=0,xN'abusez pas des break, continue, et else. Hormis pour les cas très simples, préférer les exceptions.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### La variable du FOR\n", "\n", "En Python, la variable du FOR continue à exister à la sortie du FOR (contrairement à ce qui se passe dans les langages plus stricts comme Ada ou le for-each de Java)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%reset -f\n", "\n", "sequence = [ 10, 20, 30 ]\n", "somme = 0\n", "\n", "for x in sequence:\n", " somme += x\n", " \n", "print(\"Somme = \",somme)\n", "print(\"Last = \", x)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mais **attention**, si le for itère sur un ensemble vide, la variable x n'est pas définie.\n", "\n", " - Essayez avec une séquence vide.\n", " - Il n'est pas acceptable d'échouer ainsi sur un cas trivial (l'ensemble vide). Il s'agit d'un problème d'initialisation de variable.\n", "\n", " - Conclusion : si vous souhaitez effectivement utiliser la variable de la boucle FOR en dehors de la boucle, vous devez l'initialiser avant la boucle, même si cela semble superflu.\n", "\n", "(Python est laxiste avec l'utilisation des variables, cela augmente le risque de code incorrect.)\n", "
Forcez-vous à **initialiser les variables** que vous utilisez dans plusieurs contextes.
\n", "\n", "Ici la variable x est utilisée dans le contexte du FOR et dans le contexte situé après le FOR.\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## WHILE : rien de nouveau\n", "\n", "```python\n", "while cond:\n", " bloc\n", "```\n", "\n", " - La condition est un booléen ...\n", " - ... ou une valeur quelconque qui sera considérée comme False si elle est assimilée à un objet vide (revoir la *sémantique du IF*).\n", " \n", " - Vous savez déjà très bien que la boucle s'exécute uniquement si la condition est vraie (ou assimilée à vrai).\n", " - Vous savez aussi que la condition n'est évaluée qu'à chaque retour au **début** du bloc while, mais pas à chaque instant **pendant** l'exécution du bloc. (En cas de doute, demandez à un étudiant de 1A de vous expliquer).\n", " \n", " Examinez l'exemple ci-dessous, et prévoyez ce qu'il affiche avant de l'exécuter. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%reset -f\n", "\n", "## On a besoin de ce module pour faire des pauses.\n", "import time\n", "\n", "x = 0\n", "\n", "## Boucle WHILE\n", "while x == 0:\n", " \n", " print(\"AA\")\n", " time.sleep(0.5) ## On attend un peu\n", " \n", " x = 1\n", " print(\"BB\")\n", " time.sleep(0.5) ## On attend encore un peu\n", " \n", " x = 0\n", " \n", "print(\"Terminé !\")\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Même question, prenez le temps de réfléchir." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%reset -f\n", "\n", "sequence = [ 1, 5, 2, 4, 99 ]\n", "avance = True\n", "\n", "while avance and sequence:\n", " \n", " print(\"Start : \", sequence)\n", " \n", " avance = True\n", " for x in sequence:\n", " if x > 10:\n", " avance = False\n", " \n", " ## Cette ligne retire le dernier élément de la séquence.\n", " sequence.pop()\n", " \n", " avance = True\n", " for x in sequence:\n", " if x > 10:\n", " avance = False\n", " \n", " print(\"Done.\")\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Les bad guys du while : break, continue, else\n", "\n", " - Il est possible de sortir de la boucle WHILE courante avec ```break``` ;\n", " - Il est possible de passer directement à l'itération suivante de la boucle avec ```continue``` ;\n", " - Il est possible de savoir si la boucle s'est terminée entièrement avec ```else``` \n", " (arrrh, quel mot clef [mal choisi](https://stackoverflow.com/questions/9979970/why-does-python-use-else-after-for-and-while-loops/9979985) !)\n", " \n", "Comme pour la boucle FOR, leur usage est déconseillé, sauf cas simple et idéal. Préférez en général les exceptions.\n", "\n", "Le principe fondateur du while est que la lecture de la condition doit suffire à comprendre ce qui permet de sortir de la boucle, sans avoir besoin de lire tout le bloc à l'intérieur du while.\n", "```python\n", "while condition: ## c'est ici que l'on décide comment sortir du while\n", " bloc ## et de préférence pas dans un recoin obscur du bloc intérieur\n", "```\n", "\n", "(Si votre while tient en trois lignes, un break et un continue sont tolérés car essentiellement inoffensifs.)\n", "\n", "\n", "### Propagande fonctionnelle\n", "\n", "Le *break* et le *continue* ne sont pas modulaires, i.e. ils ne peuvent pas être déplacés dans une fonction - ils doivent se trouver syntaxiquement dans la boucle. Au contraire, une levée d'exception est modulaire : on peut la déplacer dans une fonction.\n", "\n", "La définition de fonctions est un principe fondamental qui permet de factoriser le code ou de séparer proprement les étapes d'un algorithme. Un concept qui ne peut pas être capturé par une fonction doit impliquer une certaine prudence.\n", "\n", "(Nous reviendrons sur les fonctions très bientôt.)\n", "\n", "\n", "\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Exercice sur les boucles\n", "\n", "Vous devez complétez le corps de la fonction ```compare``` ci-dessous.\n", "\n", " - Il s'agit de déterminer si mot1 et mot2 sont égaux, sans prendre en considération les espaces.\n", " - Ainsi \" a b cd e \" et \"abcde\" sont considérés égaux, mais pas \"abc de\" et \"abcd\".\n", " - Quelques tests sont déjà écrits, à la fin de la fonction. Vous pouvez en ajouter.\n", " \n", "Ingrédients : un while (tiens donc), un if, un elif, un else.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%reset -f\n", "\n", "## Cette fonction compare mot1 et mot2 en ignorant les espaces.\n", "## Elle affiche un message indiquant s'ils sont égaux.\n", "def compare(mot1, mot2):\n", " \n", " ## Astuce : on ajoute une lettre à la fin de chaque mot,\n", " ## car cela facilite l'algorithme de comparaison (vous pourrez y réfléchir en temps utile).\n", " lemot1 = mot1 + \"z\"\n", " lemot2 = mot2 + \"z\"\n", "\n", " ## Index : position dans lemot1 ou lemot2\n", " index1 = 0\n", " index2 = 0\n", " \n", " ## La longueur d'un mot s'obtient avec len(lemot)\n", " ## Le i-ème caractère d'un mot s'obtient avec lemot[i]\n", " ## Le premier caractère est lemot[0]\n", "\n", " ## Ce booléen contient le résultat de la comparaison (jusqu'ici)\n", " ok = True\n", "\n", " ##\n", " ## À vous de compléter. Vous devez utiliser un WHILE (forcément, c'est l'objet du chapitre).\n", " ##\n", " \n", " # while...\n", " \n", " ##################################################################################\n", " \n", " ## À la fin, on affiche la valeur de ok.\n", " print(\"'\", mot1, \"' vs '\", mot2, \"' => \", ok)\n", " \n", "\n", "## Tests\n", "compare(\"abc\", \"abc\") ## True\n", "compare(\"a b c\", \"abc\") ## True\n", "compare(\"ab c\", \"a bc\") ## True\n", "compare(\" a bb c\", \"abc\") ## False\n", "compare(\" abc \", \" abc d\") ## False\n", "compare(\"\", \"\") ## True\n", "compare(\"a\",\"a\") ## True\n", "compare(\" a b\",\"a\") ## False\n", "\n", "\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Un peu de ménage pour terminer\n", "\n", "Attention, jupyter notebook a sans doute sauvegardé toutes les sorties des programmes ci-dessus... y compris celle de la boucle infinie. \n", "\n", "Pour nettoyer tout cela : menu **Kernel** puis **Restart & Clear Output** puis sauvegardez.\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 2 }