Nous avons extrait toute duplication de code et ajouter assez
d'abstraction pour que les règles de pluriel des noms soient définies
sous forme d'une liste de chaînes. La prochaine étape est logiquement
de mettre ces chaînes dans un fichier séparé, pour qu'elles puissent
être modifiées séparément du code qui les utilise.
D'abord, nous allons créer un fichier texte qui contient les
règles. Ici, pas de structures de données sophistiquées, seulement des
chaînes délimitées par des espaces (ou des tabulations) en trois
colonnes. Nous l'appelons rules.en,
«en» pour English. Ce sont les règles du pluriel des noms
pour l'anglais. Nous pourrons ajouter d'autres fichiers de règles pour
d'autre langues plus tard.
Exemple 17.15. rules.en
[sxz]$ $ es
[^aeioudgkprt]h$ $ es
[^aeiou]y$ y$ ies
$ $ s
Maintenant, voyons comment utiliser ce fichier de règles.
Exemple 17.16. plural5.py
import re
import string
def buildRule((pattern, search, replace)):
returnlambda word: re.search(pattern, word) and re.sub(search, replace, word) def plural(noun, language='en'):
lines = file('rules.%s' % language).readlines()
patterns = map(string.split, lines)
rules = map(buildRule, patterns) for rule in rules:
result = rule(noun) if result: return result
Nous utilisons encore la technique des fermetures
(construire dynamiquement une fonction qui utilise des variables
définies à l'extérieur de cette fonction), mais maintenant nous
avons combiné les fonctions de recherche et de transformation en
une seule fonction (la raison de cette modification apparaîtra à
la prochaine section). Cela nous permettra de faire la même chose
qu'avec les deux fonctions, mais l'appel en sera différent, comme
nous allons le voir.
Notre fonction plural prend maintenant
un second argument optionnel, language, qui
vaut en par défaut.
Nous utilisons l'argument language pour
construire un nom de fichier, puis nous ouvrons ce fichier et
copions sont contenu dans une liste. Si le langage est
en, nous allons donc : ouvrir
rules.en, le lire en entier, le segmenter à
chaque retour à la ligne et retourner une liste. Chaque ligne du
fichier est un élément de la liste.
Comme vous l'avez vu, chaque ligne du fichier contient en
fait trois valeurs, séparées par des espaces (espaces ou
tabulations ne font pas de différence). En appliquant la fonction
string.split à la liste, nous créons une
nouvelle liste dans laquelle chaque élément est un tuple de trois
chaînes. Donc, une ligne comme [sxz]$ $ es sera
segmentée en un tuple ('[sxz]$', '$', 'es').
Cela signifie que patterns contient une liste
de tuples, comme nous l'avions fait à la main à l'étape 4.
Si patterns est une liste de tuples,
alors rules sera une liste de fonctions créées
dynamiquement par chaque appel à buildRule.
L'appel à Calling buildRule(('[sxz]$', '$',
'es')) retourne une fonction qui prend un seul
argument, word. Quand cette fonction retournée
est appelée, elle exécute re.search('[sxz]$',
word) et re.sub('$', 'es', word).
Comme nous construisons maintenant une fonction combinée de
recherche et de transformation, nous devons l'appeler
différemment. Nous appelons simplement cette fonction et si elle
retourne quelque chose, c'est le pluriel du nom, si elle ne
retourne rien (None), alors la règle ne
s'applique pas et il faut essayer la règle suivante.
L'amélioration ici est que nous avons complètement séparé les
règles de pluriel des noms dans un fichier externe. Non seulement ce
fichier peut-être maintenu séparément du code, mais nous avons défini
une règle de nommage de fichier grâce à laquelle la même fonction
plural peut utiliser des fichiers de règles
différents en fonction d'un argument language.
L'inconvénient est que nous lisons le fichier à chaque fois que
nous appelons la fonction plural. Je pensais
pouvoir finir ce livre sans utiliser la phrase «laissé en exercice
au lecteur», mais c'est raté : le développement d'un mécanisme de
cache pour les fichiers de règles qui se rafraîchisse automatiquement
lorsque un fichier de règle est modifié entre deux appels est
laissé en exercice au lecteur. Amusez-vous
bien.