Les listes en compréhension

On suppose en pré-requis la connaissance et la manipulation élémentaire des listes. Au besoin, les élèves en difficulté peuvent s'aider de l'activité 0.

0. Activité : créer et manipuler une liste de notes

In [1]:
mesNotes = [20,15,0,12,14]
type(mesNotes)
Out[1]:
list
In [2]:
mesNotes[1]
Out[2]:
15
In [3]:
mesNotes[4]
Out[3]:
14
In [4]:
len(mesNotes)
Out[4]:
5
In [5]:
mesNotes
Out[5]:
[20, 15, 0, 12, 14]
In [6]:
sum(mesNotes)/5
Out[6]:
12.2

Si on remarque un erreur sur la derniere note. Comment la modifier? On veut mettre 17 à la place de 14.

In [7]:
mesNotes[4] = 17
mesNotes
Out[7]:
[20, 15, 0, 12, 17]

On dit que les listes sont mutables .

Comment ajouter une note à la liste?

In [8]:
mesNotes.append(0)
In [9]:
mesNotes
Out[9]:
[20, 15, 0, 12, 17, 0]
In [10]:
mesNotes[-1]
Out[10]:
0

Les listes sont itérables :

In [11]:
for note in mesNotes:
    print (note)
20
15
0
12
17
0

1. Activité : créer un alphabet par différentes méthodes

Dans une activité d'introduction (cryptographie), les élèves sont amenés à décrypter le texte suivant :

texte="QJSXJ NLSJR JSYIJ XUHNF QNYIJ SZRWN VZJJY XHNJS HJXNS KTWRF YNVZJ XIZHD HQJYJ WRNSF QIJQF ATNJL SWFQJ ANXJQ FUUWT UWNFY NTSIJ XKTSI JRJSY XIJQN SKTWR FYNVZ JUTZW UWUFW JWQJX QAJXZ SJUTZ WXZNY JIYZI JXIFS XQJSX JNLSJ RJSYX ZUWNJ ZWJSQ JXKTW RFSYQ FUWFY NVZJI ZSJIR FWHMJ XHNJS YNKNV ZJJYJ SIAJQ TUUFS YQJZW FUUYJ SHJUT ZWIJX FHYNA NYXIJ WJHMJ WHMJ"

Dans la résolution de cette énigme, les élèves seront aidés par la construction d'une liste contenant les lettres de l'alphabet: alphabet=['A','B','C',...,'Z']
Comment construire une telle liste ?

In [ ]:
#méthode brutale...
#on écrit tout à la main !

Essayons d'être plus subtils :
Par exemple, 64 est le code ascii décimal du caractère @
(Renseignements sur le code Ascii : https://www.rapidtables.com/code/text/ascii-table.html)

In [12]:
chr(66)
Out[12]:
'B'

Observer les codes ascii codés en décimal des lettres majuscules puis proposez un programme permettant de créer une liste contenant les lettres majuscules de l'alphabet.

In [13]:
#solution
alphabet = []
for i in range(26):
    alphabet.append(chr(65+i))
print(alphabet)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']

Nouveauté ! Observez ce que donne le code ci-dessous :

In [14]:
m = [3*n for n in range(10)]
print(m)
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

Comment cette nouvelle syntaxe pourrait simplifier votre code précédent ???

In [15]:
#solution
alphabet=[chr(i) for i in range(65,65+26)]
print(alphabet)
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']

2. Synthèse : les listes en compréhension

En programmation informatique, la syntaxe de certains langages de programmation permet de définir des listes en compréhension, c'est-à-dire des listes dont le contenu est défini à partir de tout autre type itérable (liste, tuple, chaîne de caractères…). Le résultat obtenu est toujours une liste.

Cette construction syntaxique se distingue de la construction la plus courante dans les langages de programmation qui est de définir les listes par énumération de ses éléments.

Pour résumer, une liste en compréhension vous permet d'écrire des boucles for plus concises, mais gardez à l'esprit que vous ne pouvez pas rédiger chaque boucle for en liste de compréhension.

Une compréhension de liste toute simple

Avec une boucle for

In [16]:
liste_multiplesDe2 = []
for n in range(0,16):
    liste_multiplesDe2.append(n*2)
print(liste_multiplesDe2)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]

Avec une compréhension de liste

J’ai donc utilisé une boucle for pour écrire ce petit bout de code de quatre lignes. Mais j’aurais tout aussi bien pu utiliser une compréhension de liste pour obtenir le même résultat. Voici la syntaxe de cette dernière :

In [17]:
liste_multiplesDe2 = [n*2 for n in range(0,16)]
print(liste_multiplesDe2)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30]

Syntaxe

La boucle for ci-dessus est construire simplement selon cette structure :

In [ ]:
for <the_element> in <the_iterable>:
    <the_expression>

La structure de la liste en compréhension est la suivante :

In [ ]:
[<the_expression> for <the_element> in <the_iterable>]

Si vous comparez les deux structures de l'exemple précédent, vous notez que est n, est range(0,16), et que est n*2.

Les conditions dans les listes en compréhension

Je souhaite créer une nouvelle liste de nombres pairs multipliés par 2 à partir de l'ensemble des nombres allant de 0 à 15. Il va donc falloir que j’introduise une condition pour ignorer les nombres impairs. Comparons une nouvelle fois le code écrit avec une boucle for et celui écrit avec une compréhension de liste.

Avec une boucle for

In [18]:
liste_pair = []
for n in range(0,16):
    if n % 2 == 0:
        liste_pair.append(n*2)
print(liste_pair)
[0, 4, 8, 12, 16, 20, 24, 28]

Avec une compréhension de liste

In [19]:
liste_pair = [n*2 for n in range(0,16) if n % 2 == 0]
print(liste_pair)
[0, 4, 8, 12, 16, 20, 24, 28]

J’introduis la condition à la fin de la compréhension de liste. Au lieu d’avoir cinq lignes de code, je n’en ai plus que deux.

Syntaxe

In [ ]:
[<the_expression> for <the_element> in <the_iterable> if <the_condition>]

à la place de :

In [ ]:
for <the_element> in <the_iterable>:
    if <the_condition>:
        <the_expression>

Rajout d'un else

Encore plus fort, je souhaite créer une nouvelle liste dont les chiffres pairs seront multipliés par deux, les chiffres impairs par trois. Il va donc falloir que j’introduise une condition if… else.

Avec une boucle for

In [20]:
nouvelle_liste = []
for n in range(0,16):
    if n % 2 == 0:
        nouvelle_liste.append(n*2)
    else:
        nouvelle_liste.append(n*3)
print(nouvelle_liste)
[0, 3, 4, 9, 8, 15, 12, 21, 16, 27, 20, 33, 24, 39, 28, 45]

Avec une compréhension de liste

Ça se complique un peu car il faut utiliser une expression conditionnelle qui évalue si la condition est vraie (True) ou fausse (False). Mais le résultat est au rendez-vous puisqu’au lieu d’avoir sept lignes de code, je n’en ai que deux!

In [21]:
nouvelle_liste = [n*2 if n % 2 == 0 else n*3 for n in range(0,16)]
print(nouvelle_liste)
[0, 3, 4, 9, 8, 15, 12, 21, 16, 27, 20, 33, 24, 39, 28, 45]

Syntaxe

In [ ]:
[<the_expression> if <the_condition> else <other_expression> for <the_element> in <the_iterable>]

à la place de :

In [ ]:
for <the_element> in <the_iterable>:
    if <the_condition>:
        <the_expression>
    else:
        <other_expression>

3. Exercices d'application

Exercice 1

Afficher dans une liste 10 nombres consécutifs multiples de 7.

In [22]:
#solution
lst=[7*n for n in range(12)]

Exercice 2

Compléter le code pour que la liste lst contienne l'image des nombres 0,1,2,3,4 par la fonction f.

In [ ]:
def f(x):
    return x**2+3

lst=

#solution
lst=[f(x) for x in range(5)]

Exercice 3

Afficher dans une liste tous les nombres inférieurs à 100 et divisibles par 7.

In [23]:
#solution
[n for n in range(100) if n%7 == 0]
Out[23]:
[0, 7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98]

Exercice 4

Afficher dans une liste tous les carrés parfaits inférieurs à 1000.

In [24]:
#solution
[n**2 for n in range(100) if n**2 < 1000]
Out[24]:
[0,
 1,
 4,
 9,
 16,
 25,
 36,
 49,
 64,
 81,
 100,
 121,
 144,
 169,
 196,
 225,
 256,
 289,
 324,
 361,
 400,
 441,
 484,
 529,
 576,
 625,
 676,
 729,
 784,
 841,
 900,
 961]

Exercice 5

Créez une liste contenant une uniquement les voyelles de la phrase "Mignonne allons voir si la rose".

In [25]:
voyelles = ['a','e','i','o','u','y']
phrase = "Mignonne allons voir si la rose"

#solution
n = [lettre for lettre in phrase if lettre in voyelles]
print(n)    
['i', 'o', 'e', 'a', 'o', 'o', 'i', 'i', 'a', 'o', 'e']

Exercice 6

Filtrez la liste de valeurs ci-dessous afin d'enlever toutes les valeurs égales à 0.

In [26]:
valeurs = [12, 5, 8, 32, 0, 4, 0, 0, 12, 6, 7, 0, 9, 15, 0, 0]
#solution
valeursFiltrees = [val for val in valeurs if val != 0]
print(valeursFiltrees)
[12, 5, 8, 32, 4, 12, 6, 7, 9, 15]

Exercice 7

Créez une liste contenant pour n allant de 1 à 30 :

  • n/2 si n est pair
  • 3n+1 sinon
In [27]:
#solution
syracuse=[n//2 if n%2==0 else 3*n+1 for n in range(1,31)]
print(syracuse)
[4, 1, 10, 2, 16, 3, 22, 4, 28, 5, 34, 6, 40, 7, 46, 8, 52, 9, 58, 10, 64, 11, 70, 12, 76, 13, 82, 14, 88, 15]

4. Exercices de recherche / évaluation

4.1. Sissa et l'échiquier du roi Shirham (recherche)

Selon la légende, le roi indien Shirham aurait demandé au grand vizir Sissa ben Dahir quelle récompense il souhaitait pour avoir inventé le jeu d'échecs. Sissa répondit ainsi :
« Majesté, je serais heureux si vous m'offriez un grain de blé que je placerais sur la première case de l'échiquier, deux grains pour la deuxième case, quatre grains pour la troisième, huit grains pour la quatrième, et ainsi de suite pour les soixante-quatre cases ».
« Et c'est tout ce que tu souhaites, Sissa, espèce d'idiot ? », hurla le roi abasourdi. Le roi ne se rendait pas compte du nombre de grains de blé qu'il lui faudrait offrir à Sissa !

Qu'en pensez vous?
Justifiez votre réponse en écrivant un programme donnant le nombre de grains de blé.
Remarque: vous essaierez de rédiger votre réponse avec une seule ligne de code.

In [28]:
#solution
sum([(2**i) for i in range(0,64)])
Out[28]:
18446744073709551615

4.2. Hercule et les pommes des Hespérides (évaluation)

extrait des Pydéfis de L.Signac
https://callicode.fr/pydefis/Herculito11Pommes/txt

Les Hespérides, filles d'Atlas, habitaient un merveilleux jardin dont les pommiers donnaient des pommes en or. Pour son 11e travail, Eurysthée demanda à Hercule de ramener ces pommes.

Une fois atteint le jardin merveilleux, l'oracle Nérée apprit à Hercule qu'il pourrait repartir avec une partie des pommes... à condition qu'il montre ses facultés en calcul mental. Nérée lui tint ce propos :

J'ai empilé les pommes d'or pour toi, sous la forme d'une pyramide. L'étage le plus haut ne contient qu'une pomme. L'étage juste en dessous forme un carré 2x2 (contenant 4 pommes), l'étage juste en dessous forme un carré 3x3 (contenant 9 pommes). La pyramide que tu vois contient 50 étages. L'étage de base contient donc 2 500 pommes... Je suis d'accord pour te laisser partir avec les pommes contenues dans certains étages. Précisément, si un étage contient un nombre de pommes multiple de 3, tu peux l'emporter. Si tu m'annonces combien de pommes tu emporteras au total, je te laisserai partir avec les pommes...

Pour relever ce défi, vous devez aider Hercule en lui indiquant le nombre de pommes qu'il pourra emporter. Par exemple, si la pyramide n'avait compté que 6 étages comme indiqué sur la figure suivante, chaque étage aurait été composé de : 1, 4, 9, 16, 25 et 36 pommes. Hercule aurait pu emporter les 9 pommes de l'étage 3 (car 9 est un multiple de 3) et les 36 pommes de l'étage 6 (car 36 est un multiple de 3). Au total il aurait donc emporté 45 pommes. Mais combien peut-il en emporter pour une pyramide de 50 étages ?

In [29]:
#solution
sum([n**2 for n in range(1,50) if n**2 % 3 == 0])
Out[29]:
13464