buildMatchAndApplyFunctions est une
fonction qui construit d'autres fonctions dynamiquement. Elle
prend en argument pattern,
search et replace (en fait
elle prend un tuple, mais nous expliquerons cela plus loin) et
elle construit la fonction de recherche à l'aide de
lambda comme une fonction prenant un argument
(word) et appelant
re.search avec le motif
pattern passé à
buildMatchAndApplyFunctions et l'argument
word qui lui a été passé. Ouf !
La construction de la fonction de transformation se fait de
la même manière. C'est une fonction qui prend un argument et
appelle re.sub avec les arguments
search et replace passés à
buildMatchAndApplyFunctions, ainsi que cet
argument word. Cette technique consistant à
utiliser les valeurs d'arguments externes est appelée
fermeture. Nous définissons en fait des
constantes dans la fonction de transformation que nous
construisons : elle prend un argument (word),
mais agit ensuite sur cet argument et sur deux autres valeurs
(search et replace) qui ont
été définies lorsque la fonction de transformation a été
créée.
Finalement, la fonction
buildMatchAndApplyFunctions retourne un tuple
de deux valeurs : les deux fonctions qui viennent d'être créées.
Les constantes définies dans ces deux fonctions
(pattern dans matchFunction,
search et replace dans
applyFunction) sont conservées avec ces
fonctions, y compris après le retour de l'appel à
buildMatchAndApplyFunctions. C'est
incroyablement puissant.
Si cela semble totalement confus (et ça l'est certainement, c'est
très inhabituel), la manière dont cette fonction est utilisée devrait
éclaircir ces définitions.
Nos règles de pluriel des noms sont maintenant définies
comme une série de chaînes (pas de fonctions). La première chaîne
est l'expression régulière que l'on utilise avec
re.search pour vérifier si la règle
s'applique, les deuxième et troisième sont les expressions de
recherche et de remplacement que l'on utilise avec
re.sub pour appliquer effectivement la
règle.
Cette ligne est magique. Elle prend la liste de chaînes de
patterns et la transforme en une liste de
fonctions. Comment ? En appliquant les chaînes à la fonction
buildMatchAndApplyFunctions, qui prend trois
chaînes en argument et retourne un tuple de deux fonctions. Cela
veut dire que rules contient exactement la même
chose que dans la version précédente : une liste de tuples de deux
fonctions, la première étant la fonction de recherche qui appelle
re.search et la seconde la fonction de
transformation qui appelle re.sub.
Je vous assure que c'est vrai : rules contient
exactement la même liste de fonction que dans la version précédente.
Déplions la définition de rules, nous obtenons ce qui
suit :
def plural(noun):
for matchesRule, applyRule in rules: if matchesRule(noun):
return applyRule(noun)
Puisque la liste rules est la même que
dans la version précédente, il n'est pas étonnant que la fonction
plural ne soit pas modifiée. Rappelez-vous
qu'elle est totalement générique, elle prend une liste de
fonctions de règle et les appelles dans l'ordre. Elle ne s'occupe
pas de la manière dont ces règles sont définies. À l'étape 2, elles étaient définies
comme des fonctions nommées indépendantes. À l'étape 3, elles étaient définies
comme des fonctions anonymes lambda.
Maintenant, à l'étape 4, elles sont construites dynamiquement par
la fonction buildMatchAndApplyFunctions à
partir d'une liste de chaînes. De toute manière, la fonction
plural agit de la même façon.
Au cas où tout cela ne suffirait pas à vous tourner la tête, je
dois ajouter qu'il y a une petite subtilité dans la définition de
buildMatchAndApplyFunctions que je n'ai pas
expliquée. Regardons à nouveau cette définition.
Exemple 17.13. Retour sur buildMatchAndApplyFunctions
Avez-vous remarqué les doubles parenthèses ? Cette fonction
ne prend en fait pas trois arguments mais un seul : un tuple de
trois éléments. Mais le tuple est développé lorsque la fonction
est appelée et les trois éléments sont assignés à trois variables
différentes : pattern,
search et replace. Vous vous
demandez pourquoi ? Voyons cela en détail
Exemple 17.14. Développement des tuples à l'appel de fonctions
L'appel à la fonction foo se fait avec
un tuple de trois éléments. Lorsque la fonction est appelée, les
éléments sont assignés à trois variables locales à
foo.
Maintenant, examinons pourquoi il est nécessaire d'utiliser le
développement automatique de tuple. patterns est
une liste de tuples, chacun ayant trois éléments. Lorsque nous
appelons map(buildMatchAndApplyFunctions,
patterns), cela signifie que
buildMatchAndApplyFunctions n'est
pas appelé avec trois arguments. Utiliser
map pour appliquer une liste à une fonction
appelle cette fonction avec à chaque fois un seul paramètre : chacun
des éléments de la liste. Dans le cas de patterns,
chaque élément de la liste est un tuple, donc
buildMatchAndApplyFunctions est à chaque fois
appelé avec le tuple et nous utilisons le développement automatique de
tuple dans la définition de
buildMatchAndApplyFunctions pour assigner les
éléments de ce tuple à des variables locales avec lesquelles nous
pouvons travailler.