Correction : programmation fonctionnelle

Effets de bord


def del_pairs(tab: list)->list:
	'''
	cette fonction supprime les éléments pairs présents dans une liste et renvoie la liste modifiée
	'''
	i=0
	while i < len(tab):
		if tab[i] % 2 == 0:
			tab.pop(i) # supprime l'élément d'indice i dans la liste tab
		i = i+1
	return tab

liste = [1, 2, 4, 5, 8, 7, 9, 6, 8, 10]
print(del_pairs(liste))

>>> [1, 4, 5, 7, 9, 8]
			

→ on constate que les nombres pairs ne sont pas tous supprimés, alors qu'aucune erreur n'est générée...

La cause : on modifie la liste en cours de boucle → effet de bord sur un objet mutable, la liste.

Une solution : créer une nouvelle liste à partir de la première, et laisser celle-ci inchangée :


def del_pairs(tab: list)->list:
    '''
    Cette fonction supprime les éléments pairs présents dans une liste et renvoie la liste modifiée
    '''
    tab2 = [] # ou copie = tab[:], ou utilisation du module 'copy'
    i = 0
    while i < len(tab):
        if tab[i] % 2 != 0:
            tab2.append(tab[i]) # supprime l'élément d'indice 'i' dans la liste 'copie'
        i = i+1
    return tab2

liste = [1, 2, 4, 5, 8, 7, 9, 6, 8, 10]
print(del_pairs(liste))

>>> [1, 5, 7, 9]
			

Python fonctionnel


# code n°1

def fct(i):
    if i > 0:
        return True
    else :
        return False

j = -3
fct(j)
print(j)
			

→ ainsi modifié, le code n'a plus d'effet de bord.


# code n°2

def ajout(l, i):
    return l + [i]
    
l = [4, 7, 3]
print(ajout(l, 2)) # [4, 7, 3, 2]
print(l)  # [4, 7, 3]
			

Le code de départ ne renvoie pas la même chose pour deux appels identiques, de plus il modifie la liste l. il est donc nécessaire de renvoyer une nouvelle liste, constituée de la concaténation de l et de l'élément [i] pour ne pas la modifier directement, et bien sur de la passer en argument.


# code n°3

def incremente(x):
    return x + 1

y = 5
print(incremente(y))
			

→ toujours passer la "variable" en argument de la fonction. On remarque que la fonction ne modifie pas cette variable.


# code n°4

def empile(ma_pile, valeur):
    p2 = ma_pile + [valeur]
    return p2

print(empile([], 5))
			

Même problème qu'au code n°2.


# code n°5

def volume_sphere(r):
    pi = 3.1415
    return (4/3)*pi*r**3

print(volume_sphere(4))
			

Pas de soucis ici, le code respecte bien le paradigme fonctionnel. Seulement, la variable volume n'a pas d’intérêt, on peut renvoyer directement le résultat...


# code n°6
def concatene(l1, l2):
    return l1 + l2

print(concatene([1, 2], [3, 4]))
			

Attention ici le code de départ modifie la liste l1.

La course de smileys

Version décomposée en fonction...mais pas encore fonctionnelle


from random import random

def deplacement_smiley(pos: list)->list:
    for i in range(len(pos)):
        if random() > 0.3:
            pos[i] += 1
    return pos

def affichage_smiley(pos: list, smileys: list)->None:
    for i in range(len(pos)):
        print(smileys[i] * pos[i])


def une_seconde_de_course(pos: list, smile: list)->None:
    affichage_smiley(pos,smile)
    pos = deplacement_smiley(pos)

def course(pos: list, smileys:l ist, temps: int)->None:
    while temps != 0:
        temps = temps-1
        print(temps)
        une_seconde_de_course(pos, smileys)

secondes = 10
smileys=['\U0001F600', '\U0001F60A', '\U0001F643']
positions_smiley = [1, 1, 1]

course(positions_smiley, smileys, secondes)
			

Version décomposée en fonction et presque fonctionnelle


from random import random

def f(a: int)->int:
    '''
    ATTENTION random : non fonctionnel ( pourquoi ? )
    '''
    r = random()
    if r > 0.3:
        return a + 1
    else :
        return a

def deplacement_smiley(pos: list)->list:
    tab = list(map(f, pos))
    return tab

def affichage_smiley(pos: list, smileys: list, seconde: int, i: int)->None:
    '''
    ATTENTION print : non fonctionnel
    '''
    if i == len(pos):
        print(seconde)
    else:
        print (smileys[i] * pos[i])
        return affichage_smiley(pos, smileys, seconde, i+1)

def une_seconde_de_course(pos: list, smileys: list, seconde: int, i: int)->list:
    affichage_smiley(pos, smileys, seconde, i)
    pos_suivante = deplacement_smiley(pos)
    return pos_suivante

def course(pos: list, smileys: list, temps:int, i: int)->None:
    pos = une_seconde_de_course(pos, smileys, temps, i)
    if temps != 0:
        course(pos, smileys, temps - 1, i)

secondes = 10
smile = ['\U0001F600','\U0001F60A','\U0001F643']
positions_smiley = [1, 1, 1]
i=0
course(positions_smiley, smile, secondes, i)