Des compteurs☘
Objectifs de cette page d'exercices☘
Dans cette page, les exercices portent essentiellement sur des compteurs (on compte des éléments d'une liste vérifiant une certaine propriété).
Vous essayerez de définir ces compteurs par les deux stratégies suivantes:
Stratégie 1.☘
Le compteur est obtenu par "accumulation".
- Une variable compteur est initialisée à 0.
- On parcourt ensuite les éléments de la liste et à chaque élément de la liste satisfaisant la condition imposée, on incrémente le compteur.
Stratégie 2.☘
- On essaie de définir la liste des mots satisfaisant la contrainte (liste générée par compréhension avec filtrage).
- Puis on renvoie la longueur de cette liste.
Exemple☘
Nous voulons définir une fonction prenant en paramètre une liste contenant des entiers relatifs. La fonction doit renvoyer le nombre d'entiers strictement négatifs de la liste.
Avec un compteur par accumulation
def test_compte_negatifs():
assert compte_negatifs([1,2,0]) == 0
assert compte_negatifs([1,2,0,-1, 9, -2]) == 2
def compte_negatifs(tab):
"""
tab: liste d'entiers
renvoie le nombre d'entiers <0 de tab.
"""
compteur = 0
for k in tab:
if k < 0:
compteur += 1
return compteur
Avec la longueur d'une liste
def compte_negatifs(tab):
"""
tab: liste d'entiers
renvoie le nombre d'entiers <0 de tab.
"""
return len([x for x in tab if x < 0])
Exercices☘
Au moins une☘
- Écrire une fonction python spécifiée comme suit:
def au_moins_un(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre d'éléments de la liste contenant au moins une fois
le caractère lettre
>>> au_moins_un('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
3
>>> au_moins_un('g', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
"""
Un code
def au_moins_un(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre d'éléments de la liste contenant au moins une fois
le caractère lettre
>>> au_moins_un('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
3
>>> au_moins_un('g', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
"""
compteur = 0
for mot in liste_mots:
if lettre in mot:
compteur += 1
return compteur
- Essayez de proposer une version s'appuyant sur la définition d'une liste en compréhension.
Solution
On pourrait utiliser les listes en compréhension comme suit:
def au_moins_un(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre d'éléments de la liste contenant au moins une fois
le caractère lettre
>>> au_moins_un('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
3
>>> au_moins_un('g', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
"""
return len([mot for mot in liste_mots if lettre in mot])
Le code est plus concis. Un inconvénient éventuel: on crée une seconde liste. Si la liste initiale et la liste ainsi créée sont toutes deux conséquentes, on "consomme" de la mémoire vive... alors que la version avec compteur ne crée qu'une variable supplémentaire (la variable compteur).
Exactement une☘
- Écrire une fonction python spécifiée comme suit:
def liste_exactement_un(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre d'éléments de la liste contenant exactement une fois
le caractère lettre
>>> liste_exactement_un('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
>>> liste_exactement_un('c', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
3
"""
Un code
Comme d'habitude, on doit découper en fonctions...
def chaine_exactement_un(caractere, mot):
"""
mot -- chaîne de caractères
caractère -- un caractère
renvoie True si mot présente exactement un caractère égal à caractere
et renvoie False sinon.
>>> chaine_exactement_un('a', 'arbre')
True
>>> chaine_exactement_un('a', 'abracadabra')
False
"""
compteur = 0
for carac in mot:
if carac == caractere:
compteur += 1
return compteur == 1
def liste_exactement_un(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre d'éléments de la liste contenant exactement une fois
le caractère lettre
>>> liste_exactement_un('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
>>> liste_exactement_un('c', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
3
"""
compteur = 0
for mot in liste_mots:
if chaine_exactement_un(lettre, mot):
compteur += 1
return compteur
- Proposer une version utilisant des listes définies en compréhension.
Solution
Avec la "pénalité" éventuelle signalée précédemment (consommation de mémoire):
def chaine_exactement_un(caractere, mot):
"""
mot -- chaîne de caractères
caractère -- un caractère
renvoie True si mot présente exactement un caractère égal à caractere
et renvoie False sinon.
>>> chaine_exactement_un('a', 'arbre')
True
>>> chaine_exactement_un('a', 'abracadabra')
False
"""
return 1 == len([carac for carac in mot if carac == caractere])
def liste_exactement_un(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre d'éléments de la liste contenant exactement une fois
le caractère lettre
>>> liste_exactement_un('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
>>> liste_exactement_un('c', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
3
"""
return len([mot for mot in liste_mots if chaine_exactement_un(lettre, mot)])
Au plus une☘
- Écrire une fonction python spécifiée comme suit:
def liste_au_plus_un(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre d'éléments de la liste contenant au plus une fois
le caractère lettre
>>> liste_au_plus_un('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
2
>>> liste_au_plus_un('z', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
5
>>> liste_au_plus_un('a', ['zaz', 'zut'])
2
"""
Note
On rappelle que "au plus une" signifie "0 ou 1".
Un code
Comme d'habitude, on doit découper en fonctions...
def chaine_au_plus_un(caractere, mot):
"""
mot -- chaîne de caractères
caractère -- un caractère
renvoie True si mot présente au plus une occurrence
de caractere
et renvoie False sinon.
>>> chaine_au_plus_un('a', 'arbre')
True
>>> chaine_au_plus_un('a', 'abracadabra')
False
>>> chaine_au_plus_un('a', 'biniou')
True
"""
compteur = 0
for carac in mot:
if carac == caractere:
compteur += 1
return compteur <= 1
def liste_au_plus_un(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre d'éléments de la liste contenant au plus une fois
le caractère lettre
>>> liste_au_plus_un('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
2
>>> liste_au_plus_un('z', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
5
"""
compteur = 0
for mot in liste_mots:
if chaine_au_plus_un(lettre, mot):
compteur += 1
return compteur
- Proposer une version utilisant des listes définies en compréhension.
Solution
Avec la "pénalité" éventuelle signalée précédemment (consommation de mémoire):
def chaine_au_plus_un(caractere, mot):
"""
mot -- chaîne de caractères
caractère -- un caractère
renvoie True si mot présente au plus une occurrence
de caractere
et renvoie False sinon.
>>> chaine_au_plus_un('a', 'arbre')
True
>>> chaine_au_plus_un('a', 'abracadabra')
False
>>> chaine_au_plus_un('a', 'biniou')
True
"""
return len([c for c in mot if c == caractere]) <= 1
def liste_au_plus_un(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre d'éléments de la liste contenant au plus une fois
le caractère lettre
>>> liste_au_plus_un('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
2
>>> liste_au_plus_un('z', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
5
"""
return len([mot for mot in liste_mots if chaine_au_plus_un(lettre, mot)])
Cumul des occurrences☘
Écrire une fonction python spécifiée comme suit:
def liste_nombre_total(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre cumulé des occurrences
de lettre dans les mots de liste_mots.
>>> liste_nombre_total('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
9
>>> liste_nombre_total('z', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
"""
Un code
def chaine_nombre_total(lettre, chaine):
"""
chaine -- chaîne de caractères
lettre -- caractère
renvoie le nombre d'occurrences de lettre dans chaine.
>>> chaine_nombre_total('e', 'chaînette')
2
>>> chaine_nombre_total('a', 'Barbapapa')
4
"""
compteur = 0
for carac in chaine:
if carac == lettre:
compteur += 1
return compteur
def liste_nombre_total(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre cumulé des occurrences de lettre dans les mots de liste_mots.
>>> liste_nombre_total('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
9
>>> liste_nombre_total('z', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
"""
compteur = 0
for mot in liste_mots:
compteur += chaine_nombre_total(lettre, mot)
return compteur
Un autre découpage
def chaine_nombre_total(lettre, chaine):
"""
chaine -- chaîne de caractères
lettre -- caractère
renvoie le nombre d'occurrences de lettre dans chaine.
>>> chaine_nombre_total('e', 'chaînette')
2
>>> chaine_nombre_total('a', 'Barbapapa')
4
"""
return len([c for c in chaine if c == lettre])
def somme(tab):
"""
tab: liste d'entiers
renvoie la somme des éléments de tab
"""
s = 0
for k in tab:
s += k
return s
def liste_nombre_total(lettre, liste_mots):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
renvoie le nombre cumulé des occurrences de lettre dans les mots de liste_mots.
>>> liste_nombre_total('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
9
>>> liste_nombre_total('z', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
"""
return somme([chaine_nombre_total(lettre, mot) for mot in liste_mots])
Seconde série d'exercices☘
Un nombre exact de répétition d'une lettre☘
Écrire une fonction python spécifiée comme suit:
def liste_exactement(lettre, liste_mots, n):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
n -- entier naturel
renvoie le nombre de chaînes de liste_mots
qui compte exactement n occurrences de lettre.
>>> liste_exactement('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'], 2)
2
>>> liste_exactement('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'], 5)
1
"""
Un code
def chaine_exactement(lettre, chaine, n):
"""
chaine -- chaîne de caractères
lettre -- caractère
n -- entier naturel
renvoie True si chaine présente exactement n occurrences de lettre.
>>> chaine_exactement('e', 'chaînette', 2)
True
>>> chaine_exactement('a', 'Barbapapa', 5)
False
"""
compteur = 0
for carac in chaine:
if carac == lettre:
compteur += 1
return compteur == n
def liste_exactement(lettre, liste_mots, n):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
n -- entier naturel
renvoie le nombre de chaînes de liste_mots
qui compte exactement n occurrences de lettre.
>>> liste_exactement('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'], 2)
2
>>> liste_exactement('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'], 5)
1
"""
compteur = 0
for mot in liste_mots:
if chaine_exactement(lettre, mot, n):
compteur += 1
return compteur
Code avec len...
def chaine_exactement(lettre, chaine, n):
"""
chaine -- chaîne de caractères
lettre -- caractère
n -- entier naturel
renvoie True si chaine présente exactement n occurrences de lettre.
>>> chaine_exactement('e', 'chaînette', 2)
True
>>> chaine_exactement('a', 'Barbapapa', 5)
False
"""
return len([x for x in chaine if x == lettre]) == n
def liste_exactement(lettre, liste_mots, n):
"""
lettre -- un caractère
liste_mots -- liste de chaînes de caractères
n -- entier naturel
renvoie le nombre de chaînes de liste_mots
qui compte exactement n occurrences de lettre.
>>> liste_exactement('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'], 2)
2
>>> liste_exactement('a', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'], 5)
1
"""
return len([mot for mot in liste_mots if chaine_exactement(lettre, mot, n)])
Terminaison☘
- Écrire un corps possible pour la fonction suivante:
def fin_en(syllabe, liste_mots):
"""
liste_mots -- liste de chaîne de caractères
syllabe -- chaîne de caractères
renvoie le nombre de chaînes dans liste_mots terminant par syllabe
>>> fin_en('e', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
3
>>> fin_en('er', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
>>> fin_en('er', ['permettre', 'autoriser', 'légitimer', 'accepter', 'tolérer'])
4
"""
Solution
Un code possible ci-dessous. On a décomposé la demande en deux fonctions. Rappelons qu'il faut réfléchir ainsi: décomposer les tâches complexes en petites tâches, veiller à toujours présenter des fonctions aussi simples que possible, ne réalisant qu'une seule taĉhe.
def fin_chaine_en(chaine, syllabe):
"""
chaine -- chaine de caractères
syllabe -- chaine de caractères
renvoie True si chaine finit par syllabe, False sinon.
>>> fin_chaine_en("barycentre", "re")
True
>>> fin_chaine_en("intégrale", "la")
False
"""
lgs, lgc = len(syllabe), len(chaine)
# si syllabe est plus longue que chaine, échec assuré:
if lgs > lgc: return False
# on compare les lettres de fin de chaine avec les lettres de syllabe
# en commençant par la fin des deux chaînes :
for k in range(0, lgs):
if chaine[lgc-1-k] != syllabe[lgs-1-k]:
return False
return True
def fin_en(syllabe, liste_mots):
"""
liste_mots -- liste de chaînes de caractères
syllabe -- chaîne de caractères
renvoie le nombre de chaînes dans liste_mots terminant par syllabe
>>> fin_en('e', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
3
>>> fin_en('er', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
>>> fin_en('er', ['permettre', 'autoriser', 'légitimer', 'accepter', 'tolérer'])
4
"""
compteur = 0
for mot in liste_mots:
if fin_chaine_en(mot, syllabe):
compteur += 1
return compteur
- Modifier la fonction
fin_en
de la solution précédente de façon à ce que le compteur soit obtenu comme longueur d'une liste définie en compréhension.
Solution
def fin_chaine_en(chaine, syllabe):
"""
chaine -- chaine de caractères
syllabe -- chaine de caractères
renvoie True si chaine finit par syllabe, False sinon.
>>> fin_chaine_en("barycentre", "re")
True
>>> fin_chaine_en("intégrale", "la")
False
"""
lgs, lgc = len(syllabe), len(chaine)
# si syllabe est plus longue que chaine, échec assuré:
if lgs > lgc: return False
# on compare les lettres de fin de chaine avec les lettres de syllabe
# en commençant par la fin des deux chaînes :
for k in range(0, lgs):
if chaine[lgc-1-k] != syllabe[lgs-1-k]:
return False
return True
def fin_en(syllabe, liste_mots):
"""
liste_mots -- liste de chaînes de caractères
syllabe -- chaîne de caractères
renvoie le nombre de chaînes dans liste_mots terminant par syllabe
>>> fin_en('e', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
3
>>> fin_en('er', ['abracadabra', 'entourloupe', 'machination', 'escroquerie', 'passe-passe'])
0
>>> fin_en('er', ['permettre', 'autoriser', 'légitimer', 'accepter', 'tolérer'])
4
"""
return len([mot for mot in liste_mots if fin_chaine_en(mot, syllabe)])
Plusieurs parcours de la liste... ou un seul☘
Répétition du maximum☘
Écrire une fonction python prenant en paramètres une liste de chaînes de
caractères tab
et un caractère lettre
et renvoyant un entier naturel.
L'entier naturel renvoyé est le nombre de mots dans la liste tab
qui contiennent
le plus grand nombre d'occurrences de lettre
.
Exemple:
>>> nb_occ_maxi_lettres(['aha', 'abraca', 'ah?', 'amanda'], 'a')
2
Dans cet exemple:
- la chaîne "aha" contient 2 'a',
- la chaîne "abraca" contient 3 'a',
- la chaîne "ah?" contient 1 'a',
- la chaîne "amanda" contient 3 'a'.
Le plus grand nombre de 'a' présents dans une chaîne est donc 3. Et il y a 2 chaînes contenant 3 'a': c'est ce nombre 2 qui est obtenu.
def nb_occ_maxi_lettres(tab, lettre):
"""
tab -- liste de chaînes de caractères
lettre -- caractère
renvoie le nombre de chaînes de la liste tab contenant
un nombre d'occurrences de lettre égal au maximum des nombres
d'occurrence de lettre dans les chaînes de tab.
>>> nb_occ_maxi_lettres(['aha', 'abraca', 'ah?', 'amanda'], 'a')
2
>>> nb_occ_maxi_lettres(['aha', 'abraca', 'ah?', 'amanda'], 'z')
4
"""
Solution
Une solution possible:
def nb_lettres(mot, lettre):
"""
mot -- chaîne de caractères
lettre -- caractère
renvoie le nombre d'occurrences de lettre dans mot
"""
return len([x for x in mot if x == lettre])
def nb_occ_maxi_lettres(tab, lettre):
"""
tab -- liste de chaînes de caractères
lettre -- caractère
renvoie le nombre de chaînes de la liste tab contenant
un nombre d'occurrences de lettre égal au maximum des nombres
d'occurrence de lettre dans les chaînes de tab.
>>> nb_occ_maxi_lettres(['aha', 'abraca', 'ah?', 'amanda'], 'a')
2
>>> nb_occ_maxi_lettres(['aha', 'abraca', 'ah?', 'amanda'], 'z')
4
"""
compteur = 0
maxi = nb_lettres(tab[0], lettre)
for mot in tab:
nv = nb_lettres(mot, lettre)
if nv > maxi:
compteur = 1
maxi = nv
elif nv == maxi:
compteur += 1
else:
pass
return compteur
Un autre code
def nb_lettres(mot, lettre):
"""
mot -- chaîne de caractères
lettre -- caractère
renvoie le nombre d'occurrences de lettre dans mot
"""
return len([x for x in mot if x == lettre])
def maxi(tab):
"""
tab: liste d'entiers
renvoie la valeur max de tab
"""
m = tab[0] # contiendra le max
for k in tab:
if k > m:
m = k
return m
def maxi_lettres(tab, lettre):
"""
tab -- liste de chaînes de caractères
lettre -- caractère
renvoie le nombre maximal d'occurrences de lettre
pour les chaînes de la liste tab.
>>> maxi_lettres(['aha', 'abraca', 'ah?'], 'a') # renvoie 3 car abraca présente 3 'a'
3
"""
return maxi([nb_lettres(mot, lettre) for mot in tab])
def nb_occ_maxi_lettres(tab, lettre):
"""
tab -- liste de chaînes de caractères
lettre -- caractère
renvoie le nombre de chaînes de la liste tab contenant
un nombre d'occurrences de lettre égal au maximum des nombres
d'occurrence de lettre dans les chaînes de tab.
>>> nb_occ_maxi_lettres(['aha', 'abraca', 'ah?', 'amanda'], 'a')
2
>>> nb_occ_maxi_lettres(['aha', 'abraca', 'ah?', 'amanda'], 'z')
4
"""
return len([mot for mot in tab if nb_lettres(mot, lettre) == maxi_lettres(tab, lettre)])
Compter les répétitions de chaque valeur☘
Écrire une fonction python spécifiée comme suit:
def compte_occ_entiers(liste, n):
"""
n -- entier naturel
liste -- liste d'entiers entre 0 et n
renvoie une liste compteurs telle que compteurs[i] est le nombre
d'occurrences de i dans liste (pour i entre 0 et n).
>>> compte_occ_entiers([4, 3, 4, 2], 9)
[0, 0, 1, 1, 2, 0, 0, 0, 0, 0]
>>> compte_occ_entiers([0, 1, 2, 3, 7, 8, 9], 9)
[1, 1, 1, 1, 0, 0, 0, 1, 1, 1]
"""
compteurs par accumulation
def compte_occ_entiers(liste, n):
"""
n -- entier naturel
liste -- liste d'entiers entre 0 et n
renvoie une liste compteurs telle que compteurs[i] est le nombre
d'occurrences de i dans liste (pour i entre 0 et n).
>>> compte_occ_entiers([4, 3, 4, 2], 9)
[0, 0, 1, 1, 2, 0, 0, 0, 0, 0]
>>> compte_occ_entiers([0, 1, 2, 3, 7, 8, 9], 9)
[1, 1, 1, 1, 0, 0, 0, 1, 1, 1]
"""
compteurs = [0 for i in range(0,n+1)]
for entier in liste:
compteurs[entier] += 1
return compteurs
compteurs avec len
Cette seconde solution basée sur l'utilisation de len sur des listes est peu pertinente: elle oblige à parcourir n fois la liste où n est la longueur de la liste. Elle transforme donc un algorithme de complexité linéaire en un algorithme quadratique.
def compte_occ_entiers(liste, n):
"""
n -- entier naturel
liste -- liste d'entiers entre 0 et n
renvoie une liste compteurs telle que compteurs[i] est le nombre
d'occurrences de i dans liste (pour i entre 0 et n).
>>> compte_occ_entiers([4, 3, 4, 2], 9)
[0, 0, 1, 1, 2, 0, 0, 0, 0, 0]
>>> compte_occ_entiers([0, 1, 2, 3, 7, 8, 9], 9)
[1, 1, 1, 1, 0, 0, 0, 1, 1, 1]
"""
return [len([k for k in liste if k == i]) for i in range(0,n+1)]