====== Python : programmation fonctionnelle ====== ===== Ressources ===== * https://python.developpez.com/tutoriels/apprendre-programmation-fonctionnelle/ ===== Présentation ===== Le **paradigme de programmation fonctionnelle** se caractérise essentiellement par l'**absence d'effets de bord**. Pour cela, le code défini à l'intérieur d'une fonction : * **ne dépend pas** de données se trouvant à l'extérieur de cette fonction courante ; * et le code à l'intérieur de la fonction **ne modifie pas** des données à l'extérieur de cette fonction. ===== Exemples ===== * voici une fonction **qui n'utilise pas ce paradigme de programmation fonctionnelle** : a = 0 def augmenter(): global a a = a + 1 **Explications** : * la variable **a** est définie à l'extérieur de la fonction **augmenter()** ; * Il est précisé dans le corps de la fonction **augmenter()** que la variable **a** est définie comme **globale** ; * lors de l'exécution de la fonction **augmenter()**, la valeur de la variable **a** est **changée** pour **passer de 0 à 1**. Comme il s'agit d'une variable **a** **globale**, sa valeur est maintenant **changée pour l'ensemble du programme** et pas seulement à l'intérieur de la fonction **augmenter()**. * Voici la même fonction une fonction **augmenter()** en utilisant la programmation fonctionnelle : def increment(a): return a + 1 **Explications** : * la variable **a** est passée en paramètre à la fonction **augmenter()** ; * cette fonction retourne la **valeur de a augmentée de 1** mais **sans modifier** la valeur intiale de a qui reste alors à 0. * Il n'y a pas d'effet de bord : à l'issue de l'exécution de la fonction **augmenter()**, la valeur de la variable **a n'a pas changée**. ====== Itérer sur des listes ====== La programmation fonctionnelle est particulièrement intéressant pour intervenir sur des listes. Les exemple qui suivent s'appuie sur la liste de Todos suivantes : taches = [{"id":"1","libelle":"Préparer mon sac", "accomplie":True}, {"id":"2","libelle":"Prendre mon petit-déjeuner", "accomplie":False}, {"id":"3","libelle":"Partir au lycée", "accomplie":False}] ===== La fonction map ===== * La fonction **map** prend en argument une **fonction** et une **collection de données** ; * Elle **crée** une nouvelle collection vide ; * **applique** la fonction à chaque élément de la collection d'origine et insère les valeurs de retour produites dans la nouvelle collection ; * elle renvoie alors la nouvelle collection. Voici l'utilisation de la fonction **map** pour avoir une nouvelle liste **tachesfinies** avec toutes les tâches finies : # definition de la fonction qui met la valeur True pour la donnée acccomplie def fini(tache): return {"id":tache["id"],"libelle":tache["libelle"], "accomplie":False} #nouvelle liste avec toutes taches accomplies tachesfinies = list(map(fini,taches)) Il est possible d'utiliser une **fonction anonyme lambda** directement dans la fonction map : # utilisation d'une fonction anonyme lambda #nouvelle liste avec toutes taches accomplies tachesfinies = list(map(lambda tache: {"id":tache["id"],"libelle":tache["libelle"], "accomplie":False}, taches)) Les **fonctions lambda** sont des **fonctions anonyme**s, c'est à dire des fonctions qui** n'ont pas de nom**. Une fonction anonyme : * est définie à l'aide du **mot-clef lambda** ; * les **paramètres** de la fonction lambda sont définis** à gauche du caractère deux-points** : * le **corps** de la fonction est **défini à sa droite** ; * le **résultat** de l'exécution du corps de cette fonction, ce qui correspond à l'instruction **return** est **renvoyé implicitement**. Une fonction anonyme peut être **placée** : * **directement** dans une fonction qui accepte en **paramètre** une fonction ; * dans une **variable** pour être utiliser ultérieurement. Exemple avec une variable : # definition de la fonction anonyme et affectation dans la variable fois2 fois2 = lambda x: x * 2 #utilisation >>> print(fois2(4)) >>> 8 Même résultat avec un générateur : #nouvelle liste avec les taches modifiées pour les indiquer accomplies (finies) tachesfinies = [fini(tache) for tache in taches] ===== La fonction filter ===== * La fonction **filter** prend en argument une **fonction** qui est une **condition** et une **collection de données** ; * Elle **crée** une nouvelle collection vide ; * **applique** la fonction à chaque élément de la collection d'origine et **insère ** dans la nouvelle collection uniquement les **éléments qui répondent à la condition** ; * Elle renvoie alors la nouvelle collection. Voici l'utilisation de la fonction **filter** pour avoir une nouvelle liste **tachesfinies** qui ne contient que les tâches finies : # definition de la fonction qui teste la valeur True pour la donnée acccomplie def fini(tache): return tache["accomplie"] == True #nouvelle liste avec uniquement les taches accomplies (finies) tachesfinies = list(filter(fini,taches)) Même résultat avec une fonction anonyme : #nouvelle liste avec uniquement les taches accomplies (finies) tachesfinies = list(filter(lambda tache:tache["accomplie"] == True, taches)) Même résultat avec un générateur : #nouvelle liste avec uniquement les taches accomplies (finies) tachesfinies = [tache for tache in taches if tache["accomplie"] == True] ===== La fonction reduce ===== La fonction **reduce** prend en entrée une fonction et une collection d'éléments. Elle renvoie une valeur créée en combinant les éléments de la collection. Exemple qui calcule la somme des éléments d'un tableau # importation de la fonction reduce from functools import reduce tableau = [0, 1, 2, 3, 4] somme = reduce(lambda a, x: a + x, tableau) # somme contient 10 **Explications** : * **x** est l'élément courant de l'itération et **a** est **l'accumulateur** ; * l'accumulateur est la valeur renvoyée par l'exécution de la fonction lambda sur l'élément précédent ; * la fonction **reduce()** parcourt les éléments de la liste et, pour chacun d'eux, exécute la fonction lambda sur les valeurs courantes de a et de x et renvoie le résultat qui devient le a de l'itération suivante ; * la valeur de a lors de la première itération est la première valeur de la liste ; la première valeur de x est donc le second élément de la liste. * pour préciser une valeur initiale différente de a, on le précise en 3ème paramètre de la fonction reduce : somme = reduce(lambda a, x: a + x, tableau, 50) ====== Trier des listes ====== taches.sort(key=lambda tache: tache["id"], reverse=True) * le paramètre **key** permet de préciser sur quelle données trier ; * le paramètre **reverse** permet de changer **l'ordre** du tri. ==== Retour au cours : Les instructions du langage Python ==== * [[.:c_langage_python|Cours : Les instructions du langage Python]]