<jupyter_start><jupyter_text>Syntaxe des listes en compréhension Lorsque l'on désigne *explicitement* chaque élément d'un tableau comme dans `t = [4, 3, 2, 1]`, on parle de **notation en extension** du tableau.Python connaît une autre notation pour *construire des listes* appelée **notation en compréhension**.En voici quelques exemples; **expérimenter** pour vous familiariser avec cette *syntaxe*.<jupyter_code>[None for _ in range(10)] # on peut faire plus court pour obtenir cela ...
## version plus courte du précédent
[None] * 10
[i for i in range(6)] # même résultat avec list(range(6))<jupyter_output><empty_output><jupyter_text>On peut **utiliser la variable de boucle** pour calculer<jupyter_code>[x + 1 for x in [0, 1, 4, 9, 16, 25]]
[x * x for x in range(6)]<jupyter_output><empty_output><jupyter_text>On peut combiner les deux exemples précédents en **imbriquant** ...<jupyter_code>[x + 1 for x in [i ** 2 for i in range(6)]] # la liste interne est construite en premier!<jupyter_output><empty_output><jupyter_text>L'intérêt? Supposer que vous ayez besoins des précédents (et non suivants) des carrés de 1 à 100... (pas de 0 à 99...)  À faire toi-mêmeRéalise la liste indiquée précédemment en utilisant la **notation en compréhension**.<jupyter_code># solution 1
sol1 = [x - 1 for x in [i ** 2 for i in range(1,101)]]
sol1
# solution 2
sol2 = [ x ** 2 - 1 for x in range(1, 101)]
assert sol1 == sol2 # doit passer sans erreur
print(sol2) # avec print(...) la liste est affichée différemment...<jupyter_output>[0, 3, 8, 15, 24, 35, 48, 63, 80, 99, 120, 143, 168, 195, 224, 255, 288, 323, 360, 399, 440, 483, 528, 575, 624, 675, 728, 783, 840, 899, 960, 1023, 1088, 1155, 1224, 1295, 1368, 1443, 1520, 1599, 1680, 1763, 1848, 1935, 2024, 2115, 2208, 2303, 2400, 2499, 2600, 2703, 2808, 2915, 3024, 3135, 3248, 3363, 3480, 3599, 3720, 3843, 3968, 4095, 4224, 4355, 4488, 4623, 4760, 4899, 5040, 5183, 5328, 5475, 5624, 5775, 5928, 6083, 6240, 6399, 6560, 6723, 6888, 7055, 7224, 7395, 7568, 7743, 7920, 8099, 8280, 8463, 8648, 8835, 9024, 9215, 9408, 9603, 9800, 9999]<jupyter_text>___ On peut utiliser une **fonction** avec la variable de boucle<jupyter_code>ma_fonc = lambda x: (x, x**2) # x -> (x, x**2); attend un nombre et renvoie un tuple (ce nombre, son carré)
[ma_fonc(nb) for nb in range(5)] # la fonction est exécuté à chaque changement de nb<jupyter_output><empty_output><jupyter_text>on peut faire des **sélections** avec un `if`...<jupyter_code>N = 10
t = [i for i in range(N) if i not in [3, 7]]
t<jupyter_output><empty_output><jupyter_text>Attention à l'ordre<jupyter_code># produit une erreur!
[i if i not in [3, 7] for i in range(N)] # la sélection if doit toujours avoir lieu **après** la boucle associée
a, b = 10, 20
[i for i in range(a, b) if i % 2 != 0] # i % 2 ne vaut zéro que lorsque i est pair!<jupyter_output><empty_output><jupyter_text>À faire toi-même Je voudrais une petite valse à trois temps de 100 mesures `[1,2,3,1,2,3,...]`*aide*: utiliser l'opérateur modulo `%` et un peu de sélection ... au fait, le reste d'une division est toujours strictement inférieure au diviseur ...<jupyter_code># correction: note que le reste d'une division ne peut être que 0, 1, 2, ..., diviseur-1 (toujours strictement inférieur au diviseur)
solution = [(i % 3) + 1 for i in range(300)] 
print(solution[:10]) # on ne regarde que les 10 premières valeurs
# pour tester ta solution.
from random import randint
i = 3 * randint(0, 100)
assert len(solution) == 300
assert solution[i:i+3] == [1,2,3]<jupyter_output><empty_output><jupyter_text>___  À faire toi-même Construire la liste de tous les entiers entre 50 et 100 qui ne sont ni des multiples de 2 ni des multiples de 5<jupyter_code># correction
sol = [x for x in range(50, 101) if x % 2 != 0 and x % 5 != 0]
print(sol)<jupyter_output>[51, 53, 57, 59, 61, 63, 67, 69, 71, 73, 77, 79, 81, 83, 87, 89, 91, 93, 97, 99]<jupyter_text>___ On peut utiliser plusieurs `for`<jupyter_code>[(i, j) for i in range(3) for j in range(6)]<jupyter_output><empty_output><jupyter_text>observe qu'à chaque tour de la première boucle, la seconde est complètement réalisée.Et lorsqu'on change l'ordre des deux boucles:<jupyter_code>[(i, j) for j in range(6) for i in range(3)]<jupyter_output><empty_output><jupyter_text>À faire toi-même Construire la liste de tous les mots possibles de trois lettres qu'on peut former à partir de 'a', 'b' et 'c'. Combien y en a-t-il?*aide*: - dans la boucle `for l in "abc": ...` -> la variable `l` prend successivement les valeurs `'a'`, `'b'`, `'c'`. - Penser à la **concaténation** des `str` avec `+`... Utiliser plusieurs boucles... (combien?).<jupyter_code># test de l'aide
for l in "abc":
    print(l)<jupyter_output>a
b
c<jupyter_text>**Rappel**: lorsqu'on ajoute des chaînes de caractères, le résultat est une chaîne de caractère obtenue en mettant bout à bout chaque chaîne (**concaténation**)ex: `"123"+"4"+"cinq"` donne `"1234cinq"`<jupyter_code># solution partielle pour deux lettres "ab"
sol = [l1+l2 for l1 in "ab" for l2 in "ab"]
print(sol)
# solution
sol = [l1+l2+l3 for l1 in "abc" for l2 in "abc" for l3 in "abc"]
print(sol)<jupyter_output>['aaa', 'aab', 'aac', 'aba', 'abb', 'abc', 'aca', 'acb', 'acc', 'baa', 'bab', 'bac', 'bba', 'bbb', 'bbc', 'bca', 'bcb', 'bcc', 'caa', 'cab', 'cac', 'cba', 'cbb', 'cbc', 'cca', 'ccb', 'ccc']<jupyter_text>Inutile de compter à la main ou avec `len`: il y en a $3\times 3\times 3 = 9\times 3={\bf 27}$._____  À faire toi-même Pareil que le précédent mais les mots ne doivent pas contenir deux fois la même lettre (*aide*: utiliser un if...)<jupyter_code># solution pour 2 lettres
sol = [l1 + l2 for l1 in "ab" for l2 in "ab" if l1 != l2]
print(sol)
# solution complète
[l1 + l2 + l3
 for l1 in "abc"
 for l2 in "abc"
 for l3 in "abc" 
 if l1 != l2 and # première lettre différente de la seconde,
    l2 != l3 and # la seconde de la troisième
    l3 != l1     # et la troisième de la première!
]<jupyter_output><empty_output><jupyter_text>____  Complément: difficile! On peut imbriquer les listes en compréhension à un bout ...<jupyter_code>[x + 1 for x in [x**2 for x in range(6)]] # déjà vu!<jupyter_output><empty_output><jupyter_text>ou ... à l'autre! C'est-à-dire l'**expression** peut aussi être une liste en compréhension!<jupyter_code>[[i for i in range(j, j+4)] for j in [1, 5, 9]]<jupyter_output><empty_output><jupyter_text>ou au deux ...<jupyter_code>N = 10
matrice = [
    [ i for i in range(j, j+N) ] # expression à évaluer à chaque ...
    for j in [ 1 + i*N for i in range(N) ] # ... changement de la valeur de j
]
matrice
bg, bd = 3, 18 # borne gauche, borne droite
largeur = bd - bg # ici 15
nb_subdivisions = 5 # nb de «pas» pour aller de la borne gauche à la borne droite
pas = largeur / nb_subdivisions
sub_intervalle = [bg + i * pas for i in range(nb_subdivisions + 1)]
sub_intervalle
depart = 2
taille_pas = 5
nb_pas = 12
pas = [depart + i * taille_pas for i in range(nb_pas)]
pas<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13792114695340502, 'fraction_numerical': 0.08329749103942652, 'mean_word_length': 4.671544715447155, 'pattern_counts': {'":': 2, '<': 65, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '498141', 'license': 'CC0-1.0', 'n_tokens_mistral': 3164, 'n_tokens_neox': 2750, 'n_words': 998, 'path': '01_donnees_en_tables/correction/02_syntaxe_listes_en_comprehension_correction.ipynb', 'repo_name': 'efloti/cours-nsi-premiere', 'size': '6975'}