<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 1A.1 - Dictionnaires, fonctions, code de Vigenère
Le dictionnaire est une structure de données très utilisée. Elle est illustrée pour un problème de décryptage.
Step1:  Fonctions
Les fonctions sont des portions de programmes qui reproduisent les mêmes instructions. La fonction suivante calcule un polynôme de second degré $x^2+x-5$. A chaque fois qu'on appellera la fonction polynome, elle fera le même calcul sur des x différents. Cela évite principalement d'avoir à recopier les mêmes lignes à chaque fois qu'on en a besoin.
Step2:  Une fonction commence toujours par def. Entre parenthèses, ce sont les paramètres (ou entrées de la fonction). Ce qui suit le mot-clé return est le résultat de la fonction (ou sa sortie). Parmi les fonctions, il y a celles qui existent déjà et celles que vous écrivez. La fonction cos existe déjà 
Step3:  De son utilisation ou appel 
Step4:  On peut appeler une fonction depuis une autre fonction. Une fonction peut prendre autant de paramètres que l'on veut à condition qu'ils aient des noms différents. On peut aussi leur associer une valeur par défaut 
Step5:  Les fonctions doivent porter des noms différents. Dans le cas contraire, seule la dernière existe.
Step6:  Exercice 1
Les fonctions chr et ord sont symétriques l'une de l'autre 
Step7:  Le symbol % permet d'obtenir le reste d'une division entière. L'exercice consiste à écrire une fonction qui retourne la lettre suivante dans l'ordre alphabétique. La lettre qui suit z est a.
Step8:  Fonctions dans le détail
Variable locale
Une variable créée à l'intérieur d'une fonction n'existe pas à l'extérieur 
Step9:  Mot-clé return
Sans mot-clé return, le résultat est None.
Step10:  La fonction se termine après le premier return rencontré lors de l'exécution.
Step11:  Fonction récursive
Une fonction peut être récursive 
Step12:  Dictionnaires
clé - valeur
Une liste est un ensemble d'autres objets indexés par des entiers. Un dictionnaire est un ensemble d'autres objets indexés par presque n'importe quoi.
Step13:  Quelques fonctions utiles 
Step14:  On utilise souvent un dictionnaire pour compter les lettres d'un texte 
Step15:  Les valeurs peuvent être n'importe quoi, y compris des listes ou des dictionnaires. Les clés doivent être des types immuables (nombre, chaînes de caractères, tuple incluant des types immuables). Si vous utilisez un autre type, cela déclenche une erreur 
Step16:  Lorsqu'on affecte une valeur à une clé, le dictionnaire crée la clé ou remplace la valeur précédente par la nouvelle 
Step17:  On peut aussi créer un dictionnaire de façon compacte 
Step18:  notions de coût
Il est aussi rapide d'accéder à un élément d'un dictionnaire que d'accéder à un élément d'une liste 
Step19:  A quoi ça sert ? on se sert beaucoup des dictionnaires pour compter la fréquences des caractères, des mots ou de couples de mots dans un texte. On les ordonne ensuite par fréquences décroissantes pour obtenir les mots ou caractères les plus fréquents.
Exercice 2 
Step20:  Il faut écrire une fonction qui retourne tous les mots de la liste qui ont un 'y' en seconde position.
Step21:  Exercice 3 
Step22:  Construisez le dictionnaire dictionnaire_bien_choisi pour que cela fonctionne.
Combien de mots sont stockés dans dictionnaire_bien_choisi ?
Reformulation Lorsqu'on cherche un mot dans un dictionnaire (un vrai), on tourne peu de pages pour le trouver puisqu'ils sont triés par ordre alphabétique. Maintenant, si on souhaite trouver tous les mots dans la seconde lettre est e, c'est impossible à moins de trier les mots par leur seconde lettre 
<ASSISTANT_TASK:>
Python Code:
from jyquickhelper import add_notebook_menu
add_notebook_menu()
Explanation: 1A.1 - Dictionnaires, fonctions, code de Vigenère
Le dictionnaire est une structure de données très utilisée. Elle est illustrée pour un problème de décryptage.
End of explanation
def polynome ( x ) :
    x2 = x*x
    return x2 + x - 5
Explanation: Fonctions
Les fonctions sont des portions de programmes qui reproduisent les mêmes instructions. La fonction suivante calcule un polynôme de second degré $x^2+x-5$. A chaque fois qu'on appellera la fonction polynome, elle fera le même calcul sur des x différents. Cela évite principalement d'avoir à recopier les mêmes lignes à chaque fois qu'on en a besoin.
End of explanation
def polynome ( x, coefficient ) :
    return sum ( [ x**i * c for i,c in enumerate(coefficient) ] )
Explanation: Une fonction commence toujours par def. Entre parenthèses, ce sont les paramètres (ou entrées de la fonction). Ce qui suit le mot-clé return est le résultat de la fonction (ou sa sortie). Parmi les fonctions, il y a celles qui existent déjà et celles que vous écrivez. La fonction cos existe déjà : elle fait un calcul qu'il n'est pas besoin de réécrire. La fonction polynome décrite plus haut n'existait pas avant de l'avoir définie. On distingue la définition d'une fonction :
End of explanation
y = polynome ( 1.2, [ 1, 2, -1] )  # calcul de -x^2 + 2x + 1 pour x = 1.2
y
Explanation: De son utilisation ou appel :
End of explanation
from math import log  # on importe une fonction existante
def log_base ( x, base = 10 ) :
    return log (x) / log(base)
y = log_base (1000)      # identique à y = log_base (1000, 10)
z = log_base (1000, 2)   # logarithme en base deux
y,z
Explanation: On peut appeler une fonction depuis une autre fonction. Une fonction peut prendre autant de paramètres que l'on veut à condition qu'ils aient des noms différents. On peut aussi leur associer une valeur par défaut :
End of explanation
def polynome ( x ) :       # remplacée par la seconde fonction
    return x*x + x - 5
def polynome ( x, coefficient ) :
    return sum ( [ x**i * c for i,c in enumerate(coefficient) ] )
y = polynome(4) # déclenche une exception
Explanation: Les fonctions doivent porter des noms différents. Dans le cas contraire, seule la dernière existe.
End of explanation
print ( chr( 65 ), chr (97) )
print ( ord("B"), ord ("b") )
Explanation: Exercice 1
Les fonctions chr et ord sont symétriques l'une de l'autre : elles convertissent un nombre en lettre et réciproquement.
End of explanation
def lettre_suivante(lettre) :
    # ......
    return ....
Explanation: Le symbol % permet d'obtenir le reste d'une division entière. L'exercice consiste à écrire une fonction qui retourne la lettre suivante dans l'ordre alphabétique. La lettre qui suit z est a.
End of explanation
def calcul(x) :
    y = x**2
    z = x + y
    return z
print(z)    # déclenche une exception
Explanation: Fonctions dans le détail
Variable locale
Une variable créée à l'intérieur d'une fonction n'existe pas à l'extérieur : c'est une variable locale. C'est pourquoi le code suivant provoque une erreur car la variable z n'existe pas en dehors de la fonction calcul. Les variables y ou z ne servent que d'intermédiaire de calcul. Le seul résultat qui sort de la fonction suit le mot-clé return.
End of explanation
def calcul(x) :
    y = x**2
    z = x + y
a = calcul(3)
print(a)
Explanation: Mot-clé return
Sans mot-clé return, le résultat est None.
End of explanation
def valeur_absolue(x) :
    print("je passe par ici")
    if x < 0 : 
        y = -x
        return y
    else :
        y = x
        return y
    print("je ne passe jamais par ici")
    
valeur_absolue(-5)
Explanation: La fonction se termine après le premier return rencontré lors de l'exécution.
End of explanation
def recursive(x) :
    if x / 2 < 1 :
        print("je ne m'appelle pas pour x=",x)
        return 1
    else :
        print("je m'appelle pour x=",x)
        return recursive (x/2) + 1
recursive( 10 )
Explanation: Fonction récursive
Une fonction peut être récursive : elle s'appelle elle-même. Mais il est important de savoir qu'il existe un cas dans lequel elle ne s'appelle pas pour arrêter la récursion.
End of explanation
d = { }   # un dictionnaire vide
d = { 'a' : 'acronym', 'b': 'bizarre' }  # un dictionnaire qui associe 'acroym' à 'a' et 'bizarre' à 'b'.
z = d ['a']   # z reçoit la valeur associée à 'a' et stockée dans le dictionnaire d
Explanation: Dictionnaires
clé - valeur
Une liste est un ensemble d'autres objets indexés par des entiers. Un dictionnaire est un ensemble d'autres objets indexés par presque n'importe quoi.
End of explanation
d = { 'a' : 'acronym', 'b': 'bizarre' }  
l = len(d)    # retourne le nombre d'élément de d
b = 'a' in d  # b vaut True si une valeur est associée à 'a', on dit aussi que la clé 'a' est présente 
x = d.get ('a', '')  # x vaut d['a'] si la clé 'a' existe, il vaut '' sinon 
"d=",d,"l=",l,"b=",b,"x=",x
Explanation: Quelques fonctions utiles :
End of explanation
texte = "exemple de texte"
d = { }
for c in texte :
    d[c] = d.get(c,0) + 1
d
Explanation: On utilise souvent un dictionnaire pour compter les lettres d'un texte :
End of explanation
f = [3,4]
d[f] = 0        # déclenche une exception
Explanation: Les valeurs peuvent être n'importe quoi, y compris des listes ou des dictionnaires. Les clés doivent être des types immuables (nombre, chaînes de caractères, tuple incluant des types immuables). Si vous utilisez un autre type, cela déclenche une erreur :
End of explanation
d = { }
d['a'] = 0   # création d'une clé
d['a'] = 1   # remplacement d'une valeur
d
Explanation: Lorsqu'on affecte une valeur à une clé, le dictionnaire crée la clé ou remplace la valeur précédente par la nouvelle :
End of explanation
d = { s:len(s) for s in ['un', 'deux', 'trois'] }
d
Explanation: On peut aussi créer un dictionnaire de façon compacte :
End of explanation
d = { s:len(s) for s in ['un', 'deux', 'trois', 'quatre', 'cinq'] }
d
ordonne = [ (v,k) for k,v in d.items()]
ordonne
ordonne.sort()
ordonne
Explanation: notions de coût
Il est aussi rapide d'accéder à un élément d'un dictionnaire que d'accéder à un élément d'une liste : TimeComplexity. C'est une table de hashage qui permet d'obtenir ce résultat.
ordonner les éléments d'un dictionnaire
Les éléments d'un dictionnaire ne sont pas ordonnées ou tout du moins ne le sont pas d'une façon prédictible. Pour les parcourir dans un ordre précis, il faut utiliser une liste puis les ordonner. L'exemple suivant montre comment ordonner les éléments par ordre croissant de valeur, puis par ordre alphabétique en cas d'ex aeco.
End of explanation
mots = ['eddard', 'catelyn', 'robb', 'sansa', 'arya', 'brandon',
        'rickon', 'theon', 'rorbert', 'cersei', 'tywin', 'jaime',
        'tyrion', 'shae', 'bronn', 'lancel', 'joffrey', 'sandor',
        'varys', 'renly', 'a' ]
Explanation: A quoi ça sert ? on se sert beaucoup des dictionnaires pour compter la fréquences des caractères, des mots ou de couples de mots dans un texte. On les ordonne ensuite par fréquences décroissantes pour obtenir les mots ou caractères les plus fréquents.
Exercice 2 : recherche dans une liste
On considère une liste de mots :
End of explanation
def mots_lettre_position (liste, lettre, position) :
    # ......
    return [ .... ]
Explanation: Il faut écrire une fonction qui retourne tous les mots de la liste qui ont un 'y' en seconde position.
End of explanation
def mots_lettre_position (dictionnaire_bien_choisi, lettre, position) :
    return dictionnaire_bien_choisi. get ( (position, lettre) , [] )
Explanation: Exercice 3 : utilisation d'un dictionnaire
On modifie la fonction précédente mots_lettre_position de telle sorte qu'elle s'écrive comme suit :
End of explanation
def code_vigenere ( message, cle) :
    # ...... à remplir
    return message_code
Explanation: Construisez le dictionnaire dictionnaire_bien_choisi pour que cela fonctionne.
Combien de mots sont stockés dans dictionnaire_bien_choisi ?
Reformulation Lorsqu'on cherche un mot dans un dictionnaire (un vrai), on tourne peu de pages pour le trouver puisqu'ils sont triés par ordre alphabétique. Maintenant, si on souhaite trouver tous les mots dans la seconde lettre est e, c'est impossible à moins de trier les mots par leur seconde lettre : il faudrait un dictionnaire différent pour chaque position de lettre. L'objectif de cet exercice est de concevoir une sorte de dictionnaire qui permette de retrouver tous les mots ayant telle lettre à telle position.
Exercice 4 : crypter de décrypter selon Vigenère
Le code de César est une permutation de lettre ou un décalage. Toutes les lettres du message sont décalées d'un nombre fixe :
JENESUISPASCODE
MHQHVXLVSDVFRGH
Le code de Vigenère introduit un décalage qui dépend de la position de la lettre dans le message à coder. On choisit d'abord un mot qui servira de code (par exemple DOP) puis on le traduit en décalages : [3, 14, 15] en servant de la position des lettres dans l'alphabet. 
Pour coder, on décale la première lettre de 3, la seconde de 14, la troisième 15, la quatrième de 3 à nouveau... L'objectif de cette partie est d'écrire une fonction qui crypte le message précédent et une autre qui décrypte.
End of explanation
<END_TASK>