{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# TD - Les fonctions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Exemple introductif\n",
    "\n",
    "La mention au bac se calcule en fonction de la moyenne selon les critères suivants :\n",
    "\n",
    "| Moyenne | Mention |\n",
    "| :--: | :--: |\n",
    "| Inférieur à 10 (exclu) | Aucune |\n",
    "| De 10 à 12 (exclu) | Passable |\n",
    "| De 12 à 14 (exclu) | Assez bien |\n",
    "| De 14 à 16 (exclu) | Bien |\n",
    "| De 16 à + | Très Bien |\n",
    "\n",
    "Imaginons que l'on souhaite connaitre la mention pour 2 élèves, il est possible d'écrire et d'exécuter les instructions suivantes :\n",
    "\n",
    "```python\n",
    "moyenne = 13.5 # moyenne de l'élève 1\n",
    "if moyenne < 10:\n",
    "    mention = \"Aucune\"\n",
    "elif moyenne < 12:\n",
    "    mention = \"Passable\"\n",
    "elif moyenne < 14:\n",
    "    mention = \"Assez Bien\"\n",
    "elif moyenne < 16:\n",
    "    mention = \"Bien\"\n",
    "else:\n",
    "    mention = \"Très Bien\"\n",
    "print(\"la mention pour l'élève 1 est : \", mention)\n",
    "moyenne = 10.75 # moyenne de l'élève 2\n",
    "if moyenne < 10:\n",
    "    mention = \"Aucune\"\n",
    "elif moyenne < 12:\n",
    "    mention = \"Passable\"\n",
    "elif moyenne < 14:\n",
    "    mention = \"Assez Bien\"\n",
    "elif moyenne < 16:\n",
    "    mention = \"Bien\"\n",
    "else:\n",
    "    mention = \"Très Bien\"\n",
    "print(\"la mention pour l'élève 2 est : \", mention)\n",
    "```\n",
    "\n",
    "On constate que l'on doit répéter le bloc d'instructions conditionnelles.\n",
    "\n",
    "__Y a-t-il un moyen de factoriser ce bloc d'instuctions ?__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Solution : les fonctions\n",
    "\n",
    "Utilisons la syntaxe suivante :"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "la mention pour l'élève 1 est :  Assez Bien\n",
      "la mention pour l'élève 2 est :  Passable\n"
     ]
    }
   ],
   "source": [
    "def obtenir_mention(moyenne):\n",
    "    if moyenne < 10:\n",
    "        mention = \"Aucune\"\n",
    "    elif moyenne < 12:\n",
    "        mention = \"Passable\"\n",
    "    elif moyenne < 14:\n",
    "        mention = \"Assez Bien\"\n",
    "    elif moyenne < 16:\n",
    "        mention = \"Bien\"\n",
    "    else:\n",
    "        mention = \"Très Bien\"\n",
    "    return mention\n",
    "    \n",
    "print(\"la mention pour l'élève 1 est : \", obtenir_mention(13.5))\n",
    "print(\"la mention pour l'élève 2 est : \", obtenir_mention(10.75))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$\\Rightarrow$ Le bloc calculant la mention est défini une et une seule fois !!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Déclaration d'une fonction\n",
    "\n",
    "Dans un langage de programmation, on utilise ce qu'on appelle des __fonctions__. Une __fonction__ est un ensemble d'instructions qui peut recevoir des __arguments__ et qui peut renvoyer un __résultat__ qui est souvent le contenu d'une ou plusieurs variables.\n",
    "\n",
    "En Python, on définit une fonction en utilisant l'instruction `def` (de l'anglais define, qui veut dire \"définir\") :\n",
    "\n",
    "```python\n",
    "def nom_de_la_fonction(parametre1, parametre2, ..., parametreN):\n",
    "    corps_de_la_fonction\n",
    "```\n",
    "\n",
    "- l'instruction `def` est suivie du nom de la fonction ;\n",
    "- les __paramètres__ de la fonction sont ensuite écrits entre parenthèses et séparés par des virgules ;\n",
    "- il existe des fonctions sans paramètre, les parenthèses sont néanmoins obligatoires et restent vides ;\n",
    "- il ne faut pas oublier les __deux points__ après les parenthèses de la première ligne ;\n",
    "- le corps de la fonction est un bloc d'instructions qui contient toutes les lignes qui doivent être exécutées lorsque la fonction est appelée. Le corps de la fonction doit nécessairement être __indenté__, c'est-à-dire qu'il doit être décalé d'une tabulation par rapport à l'instruction `def`.\n",
    "\n",
    "Très souvent, le corps de la fonction se terminera par l'instruction `return` suivie de la ou des valeurs que la fonction doit renvoyer. Si la fonction doit renvoyer plusieurs valeurs, celles-ci sont à séparer par des virgules.\n",
    "\n",
    "Ainsi, le schéma général d'une fonction Python est :\n",
    "\n",
    "```python\n",
    "def nom_de_la_fonction(parametre1, parametre2, ..., parametreN):\n",
    "    instructions                            # sur plusieurs lignes éventuellement\n",
    "    return valeur1, valeur2, valeur3, etc.  # souvent une fonction ne renvoie qu'une valeur\n",
    "````\n",
    "\n",
    "Exemple : Voici la fonction inverse implantée en Python\n",
    "\n",
    "```python\n",
    "def inverse(x):\n",
    "    y = 1 / x\n",
    "    return y\n",
    "```\n",
    "\n",
    "que l'on peut aussi écrire plus simplement :\n",
    "\n",
    "```python\n",
    "def inverse(x):\n",
    "    return 1 / y\n",
    "```\n",
    "\n",
    "Identifiez dans ces deux fonctions écrites différemment : leur nom, leur(s) paramètre(s), leur corps, leur(s) valeur(s) renvoyée(s).\n",
    "\n",
    "Remarque : il existe des fonctions qui ne renvoient aucune valeur, l'instruction return n'est donc pas utilisée dans le corps de ces fonctions."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Appel à une fonction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lorsque l'on exécute le code qui définit une fonction, aucune valeur n'est renvoyée ! Cela a seulement pour objectif d'enregistrer la fonction en mémoire."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# à exécuter : il ne se passera rien (visuellement)\n",
    "def inverse(x):\n",
    "    return 1 / x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour utiliser une fonction il faut l'__appeler__. On appelle une fonction par son nom en donnant des arguments (des valeurs) à ses paramètres. Dans ce cas, la fonction va renvoyer la ou les valeurs attendues."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.5"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# appel à la fonction : qui renvoie alors ce qu'il faut !\n",
    "\n",
    "inverse(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.1"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# un autre appel à la fonction : qui renvoie alors ce qu'il faut !\n",
    "\n",
    "inverse(10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Une fonction est __définie une__ et __une seule fois__,\n",
    "- À chaque appel, la fonction calcule et produit un résultat dépendant des valeurs des paramètres passés,\n",
    "- Pour un même ensemble de valeurs de paramètres, le résultat produit est toujours le même. On dit qu'une fonction est __déterministe__ (ou __pure__)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Remarque : au premier `return` rencontré l'exécution de la fonction est stoppée : si on veut renvoyer plusieurs valeurs on ne peut pas utiliser plusieurs return ; il faut séparer les valeurs à renvoyer par des virgules."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### À Faire\n",
    "\n",
    "1. Écrire une fonction `obtenir_mention` qui prend en paramètre une note, sous la forme d'un flottant et renvoie une mention, sous la forme d'une chaine de caractères.\n",
    "2. Quelles instructions permettent d'appeller la fonction pour les moyennes des 2 élèves de l'exemple introductif ?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Prototyper une fonction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Pour expliquer le fonctionnement d'une fonction, on lui ajoute un __prototype__ juste sous la ligne de définition. En Python les prototypes sont appelés _docstrings_. On peut y accéder dans le code source ou simplement en utilisant la fonction `help()`.\n",
    "\n",
    "Le prototype doit décrire le __rôle__ de la fonction, le __type des paramètres__, __type de la valeur de retour__ et les __conditions d'utilisation__."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "def inverse(x):\n",
    "    '''\n",
    "    Calcule l'inverse de x\n",
    "    :param x: (int) un entier\n",
    "    :return: l'inverse de x\n",
    "    '''\n",
    "    return 1 / x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Que se passe-t-il pour $x = 0$ ?"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Il faut a minima que le prototype contienne les cas d'utilisation et les pré-conditions sur les paramètres."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def inverse(x):\n",
    "    '''\n",
    "    Calcule l'inverse de x\n",
    "    :param x: (int) un entier\n",
    "    :return: l'inverse de x\n",
    "    :C.U: x != 0\n",
    "    '''\n",
    "    return 1 / x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on function inverse in module __main__:\n",
      "\n",
      "inverse(x)\n",
      "    Calcule l'inverse de x\n",
      "    :param x: (int) un entier\n",
      "    :return: l'inverse de x\n",
      "    :C.U: x != 0\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "help(inverse)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__N.B : Toute fonction doit comporter un prototype clair et explicite car la fonction peut être utilisée par une personne qui ne l'a pas programmé et doit savoir ce qu'elle fournit comme service sans devoir lire et comprendre son code.__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Valeur de paramètre par défaut"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. Portée des variables\n",
    "\n",
    "Les variables définies dans les fonctions, ses paramètres ou autres, sont appelés des __variables locales__, par opposition aux __variables globales__, qui sont définies dans flot d'exécution du programme."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n"
     ]
    }
   ],
   "source": [
    "# Je défini une variable globale x\n",
    "x = 3\n",
    "# Je défini une fonction avec comme paramètre x\n",
    "def double(x):\n",
    "  # Il s'agit d'une nouvelle variable locale x non liée à la variable globale x\n",
    "  return 2 * x\n",
    "\n",
    "# J'appelle la fonction double avec x = 2\n",
    "double(2)\n",
    "\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La variable globale `x` reste inchangée"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7.1. Mauvaise pratique "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n"
     ]
    }
   ],
   "source": [
    "# Je défini une variable globale x\n",
    "x = 3\n",
    "# Je défini une fonction sans paramètre\n",
    "def double():\n",
    "  # Je modifie la variable globale x\n",
    "    global x\n",
    "    x = 2 * x\n",
    "\n",
    "# J'appelle la fonction double, x = 3 à ce stade\n",
    "double()\n",
    "\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La fonction `double` ne prend aucun paramètre mais utilise une variable globale, ce qui est une mauvaise pratique car potentiellement source d'erreurs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "12\n"
     ]
    }
   ],
   "source": [
    "double()\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "24\n"
     ]
    }
   ],
   "source": [
    "double()\n",
    "print(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__N.B : Le résultat calculé et obtenu par une fonction doit dépendre et manipuler uniquement des paramètres et variables locales définis.__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Exercices\n",
    "\n",
    "- calcule le min de 2 valeurs passées en paramètre\n",
    "- calcule le nombre de voyelles d'une chaine\n",
    "- calcule le pgcd de 2 entiers\n",
    "- remplace les voyelles par des nombres\n",
    "\n",
    "- Convertir de Farenheit à Celsius et inversement\n",
    "- calculer le discriminant\n",
    "- vérification du format d'une plaque d'immatriculation\n",
    "- tortue qui trace des formes géomatriques\n",
    "\n",
    "- différence print / return"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercice X\n",
    "\n",
    "L'énergie cinétique d'un objet de masse $m$ se déplaçant à la vitesse $v$ est:\n",
    "\n",
    "$$Ec = \\frac{1}{2} m v^{2}$$\n",
    "\n",
    "Créer une fonction `energie_cinetique` qui calcule sa valeur à partir des paramètres `masse` et `vitesse`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercice X\n",
    "\n",
    "Construire une fonction, utilisant le module `random`, qui tire au sort un nombre entier entre deux bornes données en arguments."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercice X\n",
    "\n",
    "Écrire une fonction pour calculer la `factorielle` d'un nombre (un entier non négatif). La fonction accepte le nombre en tant qu'argument. \n",
    "\n",
    "Exemple: factorielle de 5: 5! = 5x4x3x2x1 = 120"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Exercice X\n",
    "\n",
    "Écrire une fonction Python qui prend un nombre en paramètre et vérifiez si le nombre est premier ou non. \n",
    "\n",
    "Remarque: Un nombre premier est un nombre naturel supérieur à 1 et qui n'a pas de diviseur positif autre que 1 et lui-même."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
