Exercices de programmation fonctionnelle.

Sommaire

Certains exercices doivent être analysés et testés (quand le code est donné), d’autres doivent être programmés par vos soins.

Remarque : en python, les fonctions map et filter renvoient des générateurs et non des listes. Cela permet de gagner du temps, puisque l’on n’a pas toujours besoin de calculer tous les éléments de la liste. Par contre, si le besoin s’en fait sentir, il est possible de transformer un générateur en liste, p. ex. maListe = list(monGenerateur) (p. ex., lorsque l’on veut afficher pour les éléments du générateurs).

Fonction map(…)

  1. Écrire la fonction mkimpair(i) qui permet d’obtenir la liste des 10 premiers entiers impairs en faisant map(mkimpair, range(0, 10)).

  2. Étant donné un mot (p. ex. mot='bonjour'), écrire un programme qui compte le nombre de voyelles du mot. Pour cela, suivez les étapes :

    1. Écrire une fonction est_voyelle(…) qui renvoie True si une lettre est une voyelle, et False sinon. Appliquer cette fonction sur le mot.

    2. Écrire une fonction bool_to_int(b) qui renvoie 1 si le booléen b vaut True, et 0 sinon. Appliquer cette fonction sur le résultat précédent.

    3. Écrire une fonction nb_voyelles(list01) qui retourne la somme de la liste list01 transmise en argument. Appliquer cette fonction sur le résultat précédent.

def est_voyelle(lettre):
    return lettre.lower() in 'aeiouy'
def bool_to_int(b):
    if b == True: return 1
    return 0
def nb_voyelles(mot01):
    return sum(mot01)

mot='bonjour'
print(list(map(bool_to_int, map(est_voyelle, mot))))
print(nb_voyelles(map(bool_to_int, map(est_voyelle, mot))))
  1. Bizarre mais potentiellement utile !
def multiply(x): return(x*x)
def add(x): return(x+x)

funcs=[multiply, add]
for i in range(2,5):
    print(list(map(lambda x: x(i), funcs)))
  1. La fonction map s’étend à plusieurs générateurs…
a = [1, 2, 3, 4]
b = [17, 12, 11, 10]
c = [-1, -4, 5, 9]
print(list(map(lambda x,y:   x+y,   a,b)))
print(list(map(lambda x,y,z: x+y-z, a,b,c)))

Fonction filter(…)

  1. Écrire une fonction qui filtre les données positives, p. ex. range(-5,5) donnera [-5, -4, -3, -2, -1].

  2. Afficher uniquement les carrés pairs des 20 premiers entiers positifs.

def est_pair(n):
    return True if n%2 == 0 else False

print(list(filter(est_pair, map(lambda x: x*x, range(1, 20)))))
  1. Il y a un cas particulier, quand la fonction est None, comme dans filter(None, [1, 2, None, 4]). Elle retourne un générateur avec la liste des éléments autres que None, i.e. [1, 2, 4].

Fonction reduce(…)

Remarque : la fonction reduce doit être importée avant d’être utilisée

from functools import reduce
  1. Calculer le factoriel d’un nombre en utilisant reduce(…).

  2. La fonction moyenne(a, b) = 0.5 a + 0.5 b n’est pas associative car moyenne(moyenne(a, b), c) != moyenne(a, moyenne(b, c)). Donc il n’est pas possible d’écrire une fonction moyenne(a,b) qui puisse être employée dans un reduce. Voici une illustration avec la liste nombres = [2 ,3 , 4]:

def moyenne(a, b):
    return 0.5*a+0.5*b

nombres = [2, 3, 4]
print(reduce(moyenne, nombres), ' != ', np.mean(nombres))

Il est néanmoins possible, en jouant sur les poids accordés à chaque valeur (en considérant des tuples (valeur, poids)), d’obtenir le bon résultat ! Étudier cet algorithme:

def moyenne_poids(tuple1, tuple2):
    v1, p1 = tuple1
    v2, p2 = tuple2
    return ( (v1*p1+v2*p2) / (p1+p2), p1+p2)

nombres = [2, 3, 4]
print(reduce(moyenne_poids, zip(nombres, [1.0]*len(nombres))), ' == ', np.mean(nombres))

Combinaison de plusieurs fonctions

  1. Programmez un algorithme qui calcule le nombre de secondes à partir d’une heure donnée au format suivant : hh:mm:ss. Ainsi 8:19:22 donnera 29962 secondes.

  2. À partir de la liste de dictionnaires suivante:

from operator import add
people=[{  'name':'Mary','height':160},
        {'name':'Isla','height':80},
        {'name':'Sam'}]
calculer la taille moyenne des personnes dont le champs _height_ est renseigné.
heights=list(map(lambda x: x['height'], filter(lambda x: 'height' in x, people)))
print(reduce(add, heights)/len(heights))
_Remarque_ : Les opérateurs standards sont définies comme des fonctions dans la librairie [operator](https://docs.python.org/fr/3/library/operator.html).
  1. Vérification d’un code ISBN10. Soit un code ISBN10 écrit sous forme d’une liste d’entiers, p. ex. isbn10 = [0,3,8,7,7,1,6,7,5,0]. La ligne print( list(enumerate(isbn10))) génère une liste de tuples (a, b), où a est le numéro de l’entier, et b sa valeur. Si la somme des produits des 2 éléments de chaque tuple est divisible par 11, alors isbn10 est un code valide.
from operator import add

def pdt(tupleab):
    return tupleab[0]*tupleab[1]

isbn10 = [0, 3, 8, 7, 7, 1, 6, 7, 5, 0]
print(list(enumerate(isbn10)))
print(list(map(pdt, enumerate(isbn10))))
print(reduce(add, map(pdt, enumerate(isbn10))) % 11)
# le reste de la division entière par 11 valant 0, le code est bien de type ISBN10
  1. Vérification d’un code ISBN13. Soit un code ISBN13 écrit sous forme d’une liste d’entiers, p. ex. : isbn13=[9,7,8,2,8,2,2,7,0,1,6,2,4]. Le dernier chiffre est un code de vérification selon une méthode à peine plus complexe. Écrire la fonction poids13(…) qui reçoit un couple (i, v) en paramètre et qui retourne v si i est pair et v*3 si i est impair. Appliquez cette fonction à enumerate(isbn13), la somme des nombres doit être un multiple de 10.
from operator import add

def poids13(tupleiv):
    i, v = tupleiv
    return v if i%2 == 0 else v*3

isbn13 = [9, 7, 8, 2, 8, 2, 2, 7, 0, 1, 6, 2, 4]
print(list(enumerate(isbn13)))
print(list(map(poids13, enumerate(isbn13))))
print(reduce(add, map(poids13, enumerate(isbn13))) % 10)
# le reste de la division entière par 10 valant 0, le code est bien de type ISBN13