Petit exercice en Python


Nouveau concept, de temps en temps, je vais proposer un exercice à faire en Python, vous postez vos solutions, et j’en posterai une le lendemain.

Pas de pression, c’est pour le fun.

Les solutions dans un autre langage sont bienvenues, mais privilégiez Python si vous avez le choix, c’est quand même le but.

Postez votre code sur un pastebin ou ailleurs avec un lien en commentaire, pas le code directement dans les commentaires.

Exercice du jour :

Un script qui attend un fichier en paramètre, l’ouvre, et trouve toutes les positions de chaque mot.

Le script doit prendre en compte les apostrophes, supprimer la ponctuation, et normaliser la casse et les caractères spéciaux des mots.

Le résultat doit afficher une liste à de mots avec leurs positions ordonnée par le nombre d’apparition, ou en cas d’égalité, par ordre naturel des positions.

Aucune gestion d’erreur n’est demandée.

Par exemple si j’ai un fichier contenant :

« La marche des vertueux est semée d’obstacles qui sont les entreprises égoïstes que fait sans fin surgir l’œuvre du Malin. Béni soit-il l’homme de bonne volonté qui, au nom de la charité, se fait le berger des faibles qu’il guide dans la vallée d’ombre, de la mort et des larmes, car il est le gardien de son frère et la providence des enfants égarés. J’abattrai alors le bras d’une terrible colère, d’une vengeance furieuse et effrayante sur les hordes impies qui pourchassent et réduisent à néant les brebis de Dieu. Et tu connaîtras pourquoi mon nom est l’éternel quand sur toi s’abattra la vengeance du Tout-Puissant ! »

Ça fait des années que je répète ça. L’enfoiré qui l’entend, il meurt aussitôt. J’avais jamais cherché à comprendre, je trouvais seulement que ça en jetait de dire ça avant de flinguer un mec. Et puis ce matin, j’ai vu quelque chose qui m’a fait réfléchir. D’un seul coup, je me dis, ça pourrait bien vouloir dire que tu es l’œuvre du malin, et que l’homme vertueux c’est moi, et que mon joli 9 mm ce serait mon protecteur, mon berger dans la vallée de l’angoisse et des larmes. Ou encore mieux, c’est moi le berger et toi l’homme vertueux, et c’est le monde qui est l’œuvre de Lucifer. Qu’est-ce que tu dis de ça ? Mais rien de tout ça n’est juste. Ce qui est vrai, c’est que tu es le faible et que je suis la tyrannie des méchants. Et moi j’essaie, Ringo, au prix d’un effort harassant, de protéger les faibles.

On appelle le script ainsi :

python ton_script.py ton_fichier.txt

Et il affiche ceci :

- marche: 1
- semee: 5
- obstacles: 7
- sont: 9
- entreprises: 11
- egoistes: 12
- sans: 15
- fin: 16
- surgir: 17
- beni: 22
- soit: 23
- bonne: 28
- volonte: 29
- charite: 35
- se: 36
- guide: 44
- ombre: 49
- mort: 52
- car: 56
- gardien: 60
- son: 62
- frere: 63
- providence: 66
- enfants: 68
- egares: 69
- abattrai: 71
- alors: 72
- bras: 74
- terrible: 77
- colere: 78
- furieuse: 82
- effrayante: 84
- hordes: 87
- impies: 88
- pourchassent: 90
- reduisent: 92
- neant: 94
- brebis: 96
- dieu: 98
- connaitras: 101
- pourquoi: 102
- eternel: 107
- quand: 108
- s: 111
- abattra: 112
- puissant: 117
- annees: 121
- repete: 124
- enfoire: 127
- entend: 130
- meurt: 132
- aussitot: 133
- avais: 135
- jamais: 136
- cherche: 137
- comprendre: 139
- trouvais: 141
- seulement: 142
- en: 145
- jetait: 146
- avant: 150
- flinguer: 152
- mec: 154
- puis: 156
- matin: 158
- ai: 160
- vu: 161
- quelque: 162
- chose: 163
- m: 165
- reflechir: 168
- seul: 171
- coup: 172
- me: 174
- pourrait: 177
- bien: 178
- vouloir: 179
- joli: 199
- mm: 200
- serait: 202
- protecteur: 204
- angoisse: 212
- ou: 216
- encore: 217
- mieux: 218
- monde: 233
- lucifer: 239
- mais: 248
- rien: 249
- n: 253
- juste: 255
- vrai: 259
- faible: 266
- suis: 270
- tyrannie: 272
- mechants: 274
- essaie: 278
- ringo: 279
- prix: 281
- effort: 284
- harassant: 285
- proteger: 287
- malin: 21, 187
- au: 31, 280
- nom: 32, 104
- faibles: 41, 289
- qu: 42, 240
- dans: 45, 207
- vallee: 47, 209
- larmes: 55, 215
- une: 76, 80
- vengeance: 81, 114
- sur: 85, 109
- toi: 110, 225
- tout: 116, 251
- dire: 148, 180
- dis: 175, 245
- es: 183, 264
- vertueux: 3, 192, 228
- oeuvre: 19, 185, 237
- du: 20, 115, 186
- homme: 26, 191, 227
- berger: 39, 206, 223
- a: 93, 138, 166
- un: 153, 170, 283
- moi: 195, 221, 276
- les: 10, 86, 95, 288
- fait: 14, 37, 119, 167
- il: 24, 43, 57, 131
- j: 70, 134, 159, 277
- tu: 100, 182, 244, 263
- mon: 103, 198, 203, 205
- je: 123, 140, 173, 269
- ce: 157, 201, 242, 256
- c: 193, 219, 230, 260
- d: 6, 48, 75, 79, 169, 282
- le: 38, 59, 73, 222, 232, 265
- des: 2, 40, 54, 67, 120, 214, 273
- qui: 8, 30, 89, 128, 164, 234, 257
- ca: 118, 125, 144, 149, 176, 247, 252
- la: 0, 34, 46, 51, 65, 113, 208, 271
- que: 13, 122, 143, 181, 189, 197, 243, 262, 268
- l: 18, 25, 106, 126, 129, 184, 190, 211, 226, 236
- est: 4, 58, 105, 194, 220, 231, 235, 241, 254, 258, 261
- de: 27, 33, 50, 61, 97, 147, 151, 210, 238, 246, 250, 286
- et: 53, 64, 83, 91, 99, 155, 188, 196, 213, 224, 229, 267, 275

Solution

33 thoughts on “Petit exercice en Python

  • zanguu

    Le script doit prendre en compte les apostrophes

    Euh, désolé si j’suis con, mais ça veux dire qu’on considère les apostrophes comme des espaces (“c’est” = 2 mots) ou comme un caractère (“c’est” = 1 mot).

    Dans l’exemple c’est la première hypothèse mais à la lecture j’avais compris la deuxième.

  • foxmask

    vu le résultat escompté je dirai que oui ‘ = espace (puisqu’on a du l, c, d tout seul dans le resultat final)

  • bob

    Bonjour,

    Je considère que 9 est un mot, d’où le décalage de 1 après «9 mm» par rapport à ce que tu obtiens

    Je ne formate pas la sortie pour mettre ma liste avec des virgules parce-que j’ai pas envie.

    http://pastebin.com/U5uxY2SH

  • Sam Post author

    @zanguu : “c’est” est une contraction de deux mot : “cela est”, donc c’est bien deux mots. Les apostrophes ont je crois été inventé pour pallier syntaxiquement à l’évolution de la langue orale.

  • bob

    @ Sam

    Le defaultdict est une structure de données ultra-pratique, c’est ma préférée.

    Pour le nettoyage des caractères (que j’ai torché à la gros porc), je pense que la fonction slugify de django serait un bon départ.

  • frodon1

    @foxmask: La clé du dictionnaire est un tupple (len_mot, mot). Ici le dictionnaire est parsé selon les clés triées, le critère de tri étant ce tupple, donc de la plus petite longueur à la plus longue.

    J’ai bon ?

  • frodon1

    Est-il possible de supprimer le 1er post (nom de Foxmask écorché) ?

  • bob

    @frodon1
    t’as bon :)

    @foxmask

    l’itérateur dictword.iteritems() renvoie à chaque itération la paire clé-valeur notée (k,v).


    »»» for item in dictword.iteritems():
    print item

    ('jetait', [146])
    ('a', [93, 138, 166])
    ('brebis', [96])
    ('larmes', [55, 216])
    ...

    la lambda fonction crée à partir de cette paire une clé notée key :


    »» f = lambda (k,v):(len(v),v)

    »» f(('jetait', [146]))
    (1, [146])

    »» f(('a', [93, 138, 166]))
    (3, [93, 138, 166])

    La fonction sorted() prend pour argument l’itérateur ainsi que la clé key constituée par la lambda fonction pour faire le tri.

  • foxmask

    ok ; j’avais pas vu ce que retournait defaultdict(list) ; ce qui correspond pile poil à l’exercice, mais j’etais resté à defaultdict(int).
    merci !

  • zanguu

    @sam, http://en.wikipedia.org/wiki/Typographic_ligature#Ligatures_in_Unicode_.28Latin-derived_alphabets.29
    En gros là ça pose soucis à cause du mot œuvre car le “e dans l’o” (il me semble qu’on dit comme ça en français) est une ligature qui possède son propre caractère unicode (U+0153) et du coup normalize() ne le transforme pas en “oe”, ce qui serait syntaxiquement faux.
    Le problème c’est que si tu veux passer ça dans d’autres encodages, la correspondance n’existe pas forcement et tu as le choix entre mettre un “?” ou effacer le caractère.

    Comme je l’avais dit dans mon post (que je fait du boulot juste avant de partir mais qui apparemment n’est pas passé) on n’a pas besoin de s’en soucier quand on utilise unidecode mais ça fait une lib à installer.

    @Réchèr, j’ai déjà eu plus petit comme spec pour un truc beaucoup plus gros/important (et la moitié était fausse) alors là franchement ça passe. Au pire tu testes si le fichier est en utf-8 et si non, ben tu le bascules à la lecture et voilà ^^

  • zanguu

    Ma contribution :
    http://pastebin.com/UKPXS2r9 (valable 1 mois)

    clean_text pourrait être réduite en utilisant unidecode mais flemme de l’installer.
    (phoque! ce message aurais du être envoyé le 16 à 18h)

  • Sam Post author

    @Réchèr : j’ai simplifié l’exercice en virant toute gestion d’erreur. Sinon faut aussi gérer les erreurs de parsing d’arguments de la ligne de commande, l’encoding d’entrée ET de sortie, etc. Ca rend le script beaucoup plus lourd, et c’est pas le but.

  • adrix12

    Bon deux jours après, si quelqu’un lis ça tant mieux sinon je me serai quand même bien amusé.
    J’ai fait ça en bash et j’ai appris pas mal de truc sur les possibilités de bash, les regex et la toolbox linux.

    http://pastebin.com/iHtukYt1

    Le code est crade mais ça marche. Au début c’est décalé de 1 car le comptage des mots commence à 1 et après ça décale d’un de plus car 9 est considéré comme un mot.

    Voilà ! :-)

    • Sam Post author

      Pas de problème, mais faut que change le asort, il ne trie pas par le bon critère.

Comments are closed.

Des questions Python sans rapport avec l'article ? Posez-les sur IndexError.