|
|
Nous allons
maintenant compléter notre outil d'analyse en y ajoutant les fonctionnalités
suivantes : Pour analyser les occurrences, nous continuerons à utiliser le bouton dont nous nous servions pour faire les statistiques du texte. Ce bouton fonctionnera en bascule : lorsqu'il aura fait les statistiques, le bouton proposera d'analyser les occurrences, lorsque les occurrences auront été analysées, le bouton proposera à nouveau de faire les statistiques, etc. La légende de ce bouton devra changer en fonction de l'état de l'application.
|
Pour obtenir le résultat décrit ci-dessus, nous allons devoir modifier et compléter le script du deuxième gros bouton, qui ne nous servait jusque là qu'à faire les statistiques du texte.
Deux affichages
sont possibles dans la fenêtre de résultat :
- les statistiques du texte,
- les occurrences et leur fréquence.
Ceci veut dire
que le bouton va déclencher alternativement la méthode faireStats(),
ou la nouvelle méthode, que nous allons construire plus loin, qui permet
d'analyser les occurrences, et que j'ai appelée dans l'exercice chercheoccurrences().
Nous retrouvons donc bien ici la notion de mode que nous avions mise en place
dans l'exercice de l'avion : dans l'un des modes, numéroté 1,
le bouton fait les statistiques du texte, dans l'autre, numéroté
2, le bouton analyse les occurrences.
D'autre part, pour guider notre utilisateur, la légende du bouton doit
changer : lorsque les statistiques sont affichées, le bouton doit montrer
comme légende "Analyser les occurrences...", et lorsque
rien n'est affiché (au démarrage) ou que les occurrences sont affichées,
le bouton doit montrer comme légende "Faire les statistiques
du texte...".
En effet, cette légende indique ce qui se passera (dans
le futur) si on clique sur le bouton.
Le bouton doit donc changer de mode quand on le clique, c'est à dire qu'on en fait une bascule : un premier clic, le mode étant 1, et je fais les statistiques, un deuxième clic, le mode ayant été mis à 2 par le clic précédent, et j'analyse les occurrences, un troisième clic, le mode étant revenu à 1 du fait du clic précédent, et je refais les statistiques, etc.
Ainsi, on trouvera
des modifications dans le script du gros bouton (celui qui passe des statistiques
aux occurrences et vice-versa) aux niveaux suivants:
- ajout de propriétés (au moins leMode
dans un premier temps mais nous verrons que nous devrons en créer d'autres
dont nous découvrirons la nécessité à mesure que
nous progresserons dans la construction des fonctionnalités),
- modification du beginSprite (ne serait-ce que parce que, du fait
de l'apparition de nouvelles propriétés, ces dernières
devront être initialisées, mais il nous faudra aussi donner des
instructions de présentation d'interface),
- modification du mouseUp,
- ajout d'une méthode pour la recherche des fréquences d'occurrence,
chercheoccurrences(), méthode que, comme à notre habitude,
nous allons écrire en la laissant vide pour l'instant.
A noter que la méthode existante faireStats(uneChaîne, unTexte),
elle, ne change pas, car nous n'introduisons aucune modification dans cette
fonctionnalité.
Nous allons donc pouvoir réécrire notre script complet de la manière suivante, en pseudo-code :
property spriteNum, leMode
on
beginSprite
mettre
leMode à 1
afficher
comme légende du bouton "Faire les statistiques du texte"
end
on
mouseEnter, on mouseLeave et on mouseDown
sans
changement
end
on mouseUp
remettre
tout le texte en style normal et en gras
Dans
le cas où leMode vaut
![]()
1
:
![]()
![]()
faireStats()
sur le texte contenu dans l'acteur "leTexte"
![]()
![]()
changer
la légende du bouton en "Analyser les occurrences..."
![]()
![]()
mettre
leMode à 2
![]()
2
:
![]()
![]()
chercher
les occcurences dans le texte contenu dans l'acteur leTexte, en appelant la
méthode chercheoccurrences()
![]()
![]()
changer
la légende du bouton en "Faire les statistiques du texte..."
![]()
![]()
mettre
le mode à 1
![]()
fin
des cas
end
on
faireStats(unTexte)
sans
changement
end
on
chercheoccurrences me
vide
pour l'instant
end
Remarques :
- leMode du bouton est mis à 1 dans le beginSprite,
et sa légende est fixée à "Faire
les statistiques du texte". En effet, au démarrage de
l'application, et/ou au moment où on utilisera la première fois
ce bouton "stats/occurrences", le champ resultat est soit
vide (on n'a encore rien touché), soit contient les résultats
d'une recherche de sous-chaîne (si on a utilisé cette fonctionnalité
auparavant). Donc, dans tous les cas, au moment de se servir la première
fois de ce bouton, sa mission est de faire les statistiques du texte.
- en première ligne du mouseUp, on remet en noir et en style
normal le champ leTexte. En effet, si on a par hasard fait une recherche
de sous-chaîne avant de faire les statistiques du texte, il ne faut pas
que les caractères qui auraient été mis en rouge et en
gras par la recherche, le restent. Donc dans tous les cas, on rectifie la couleur
et le style du texte, et la ligne d'instructions correspondantes est placée
avant d'entrer dans l'examen de la valeur de leMode.
Ce qui nous donnera en code :
property spriteNum, leMode
on
beginSprite
leMode
= 1
member("legende2").text
= "Faire les statistiques du texte..."
end
on
mouseEnter, on mouseLeave et on mouseDown
sans
changement
end
on mouseUp
set
the foreColor of member("leTexte") to the forecolor of member("noirRef")
set
the fontStyle of member("leTexte") to "plain"
case
leMode of
![]()
1
:
![]()
![]()
faireStats(member("leTexte").text)
![]()
![]()
member("legende2").text
= "Analyser les occurrences..."
![]()
![]()
leMode
= 2
![]()
2
:
![]()
![]()
chercheoccurrences()
![]()
![]()
member("legende2").text
= "Faire les statistiques du texte..."
![]()
![]()
leMode
= 1
end
case
end
on
faireStats(unTexte)
sans
changement
end
on
chercheoccurrences me
Vide
pour l'instant
end
Voyons maintenant quel va être le traitement que nous allons faire dans la méthode chercheoccurrences().
Dans la précédente
méthode faireStats(), nous avions "haché" notre
texte de manière à en eXtraire tous les mots, débarassés
de tous les autres signes (ponctuations, espaces, sauts de ligne, etc.).
Dans la liste des mots que nous en avions alors constituée (lstMots),
les mots figurent autant de fois qu'ils sont employés dans le texte.
Cette liste de mots comprend donc des doublons.
Dans la recherche de fréquence des occurrences, nous cherchons à
savoir combien de fois chaque mot différent figure dans la liste des
mots.
Cela veut dire :
- que nous allons devoir éliminer les doublons pour ne garder qu'un seul
exemplaire de chaque mot différent (on dit "dédoublonner"
: sport favori des gestionnaires de bases de données où les doublons
sont un fléau !),
- mais que, dans le même temps, il nous faudra compter le nombre d'exemplaires
de chaque mot différent, et trouver un moyen de garder associés
tel mot et son nombre d'occurrences. Chaque mot devra être lié de
manière fixe et définitive à sa fréquence.
Deux questions
se posent alors :
- trouver un modèle d'entité qui nous permette de modéliser
une liste de couples mot/fréquence,
- ce modèle d'entité doit permettre la manipulation de ces couples
sans jamais séparer le mot de sa fréquence.
Avant de nous intéresser au traitement de dédoublonnage et de comptage, voyons quels pourraient être les modèles d'entité que nous pourrions adopter pour stocker nos couples mot/fréquence.
Tout d'abord,
mentionnons que nous allons devoir trier la liste des mots.
Nous allons devoir la trier d'une part pour la présentation des résultats
de l'analyse des occurrences, puisque nous présentons ce résultat
par ordre alphabétique, et d'autre part, nous verrons plus bas que le
tri préalable, sans être totalement indispensable au traitement
de dédoublonnage, le simplifie considérablement.
Il existe heureusement une instruction dans les langages qui permet de trier
une liste ou un tableau.
Si la liste ou le tableau contient des chaînes de caractères, c'est
à dire des valeurs alphanumériques, alors les valeurs de la liste
ou du tableau sont triées par ordre alphabétique croissant.
Si la liste ou le tableau contient des valeurs numériques, alors le tri
s'effectue par ordre de valeurs numériques croissantes.
Si la liste contient un panachage de chaînes et de valeurs numériques
(ce qui est possible en Lingo mais pas forcément dans d'autres langages
où les tableaux doivent être homogènes en terme de types
de données), alors la liste, un fois triée, contient au début
toutes les valeurs numériques triées par ordre croissant, et ensuite,
toutes les chaînes triées par ordre alphabétique croissant.
Si la liste contient d'autres listes, alors le tri s'effectuera sur la première
valeur des listes qui constituent les éléments de la liste principale,
et ceci selon les même règles que pour des éléments
simples.
Cette instruction, en lingo, s'appelle sort(). C'est
une méthode d'objet liste, donc nous écrirons au moment de trier
la liste nommée uneListe par exemple : uneListe.sort()
Maintenant que notre liste est triée, elle contient toujours tous les mots, mais les mots identiques sont regroupés et figurent dans la liste les uns à la suite des autres. Voici le début de la liste des mots, tout d'abord non triée, puis le début de cette même liste, une fois triée (pour plus de lisibilité, nous avons mis les groupes de mots identiques en couleur et en gras) :
- non triée : ["Le", "meurtre", "de", "Calas", "commis", "dans", "Toulouse", "avec", "le", "glaive", "de", "la", "justice", "le", "9", "mars", "1762", "est", "un", "des", "plus", "singuliers", "événements", "qui", "méritent", "l", "attention", "de", "notre", "âge", "et", "de", "la", "postérité", "On", "oublie", "bientôt", "cette", "foule", "de", "morts", "qui", "a", "péri", "dans", "des", "batailles", "sans", "nombre", "non", "seulement", "parce", "que", "c", "est", "la", "fatalité", "inévitable", "de", "la", "guerre", "mais", "parce", "que", "ceux", "qui", ...
- triée : ["1762", "9", "a", "a", "a", "a", "a", "a", "à", "à", "à", "à", "à", "à", "à", "à", "à", "à", "à", "à", "à", "à", "à", "à", "abjuration", "abjuration", "abjuré", "absurde", "accusé", "acquittaient", "affaiblit", "affaire", "âge", "âgé", "agissait", "agissait", "aider", "aîné", "ainsi", "ajoutèrent", "alla", "allaient", "alors", "Alors", "ami", "ami", "ami", "ami", "ami", "amis", "années", "ans", "ans", "ans", "ans", "Antoine", "Antoine", "Antoine", "Antoine", "Antoine", "Antoine", "Antoine", "Antoine", "apoplectique", "approuva", "après", "après", "Après", ...
NB
: les occurrences "1762" et "9" ne sont pas traitées
comme des valeurs numériques puisqu'elles font partie d'une chaîne
de caractère. On constate néanmoins deux choses :
- les chiffres traités comme caractères viennent en tête
de l'ordre alphabétique,
- pour les caractères de "0" à "9", le tri
par ordre alphabétique correspond au tri par valeurs numériques.
Mais pour les nombre de plus d'un chiffre, alors c'est la suite des caractères
numériques qui est prise en compte et non pas la valeur numérique
du nombre. Ainsi, "1762", qui commence par "1" vient avant
"9", alors que la valeur numérique de 1762 est plus grande
que la valeur numérique de 9.(*)
D'autre part, remarquons que le tri par ordre alphabétique n'est pas
sensible à la casse, et les capitales ainsi que les bas de casse sont
traitées dans le même ordre.
Dernière
mention à propos des tris : une fois qu'une liste est triée, elle
le reste. Si on ajoute des éléments supplémentaires à
la liste, ceux-ci se placeront à l'endroit qui va bien du point de vue
du tri, et non pas à la fin de la liste, comme cela se passe avec une
liste non triée.
La seule manière de "détrier" une liste, c'est d'écrire
un traitement que nous avons déjà rencontré dans l'exercice
du mémory, où il nous fallait, pour initialiser le jeu, "battre
les cartes".
Voyons maintenant comment dédoublonner la liste et compter les occurrences.
Nous avons dit
plus haut qu'il nous fallait stocker ensemble les couples mots/nombre d'occurrences,
sans qu'on puisse désolidariser l'un de l'autre, c'est à dire
que chaque mot doit être attaché de manière permanente et
sure à son nombre d'occurrences. D'autre part, comme nous avons un grand
nombre de mots, l'entité la mieux adaptée parait être une
liste.
Lingo nous fournit un modèle de liste dans lequel on peut stocker des
éléments à 2 valeurs : ce sont les listes de propriétés.
Les listes de propriétés sont des listes qui savent stocker des
couples de valeurs, lesquelles, du point de vue syntaxique, sont séparées
par le signe deux-points ( : ). Dans la liste, chaque couple est séparé
par une virgule comme dans les listes que nous avons déjà utilisées,
et qui sont appelées en lingo des listes linéaires.
La première valeur du couple (à gauche des ":") est
appelée propriété, la deuxième (donc à
droite des ":") est appelée valeur.
Nous allons donc stocker chaque mot différent en propriété, et chaque nombre d'occurrences en valeur associée au mot.
ATTENTION : les propriétés employées à propos des listes n'ont RIEN A VOIR avec les propriétés de sprite que nous utilisons déjà depuis le début de ce cours. Même si ces deux entités portent le même nom générique, il faut faire très attention à ne pas les confondre. Le contexte doit indiquer si on parle de l'une ou de l'autre, sans quoi il faudra toujours préciser.
Les deux autres
solutions auraient pu être :
- deux listes linéaires dont les éléments se correspondent
rang à rang. Ce modèle ne convient pas car le tri casserait la
correspondance de rang à rang. Ce modèle convient lorsque les
listes, une fois constituées restent toujours dans le même ordre.
- une liste linéaire dont les éléments seraient chacun
une autre liste linéaire de deux éléments : le mot et sa
fréquence. Ce modèle peut convenir. Les manipulations sont peut-être
un peu plus lourdes qu'avec une liste de propriétés, mais il fonctionnerait.
Dans l'exercice, nous avons choisi d'utiliser les listes de propriétés,
ne serait-ce pour en avoir entendu parler.
NB
: Les listes de propriétés ne sont pas forcément aisées
à manipuler, notamment lorsqu'il est question de les trier en divers
ordres. En effet, s'il est possible de les trier par ordre de propriétés,
on ne peux pas les trier par ordre de valeurs sans un traitement qu'il nous
faut écrire nous-mêmes : nous aurons ce problème lorsque
nous voudrons présenter les résultats triés par ordre d'occurrences,
à l'étape suivante. Mais dans le cas présent, les listes
de propriétés répondent à nos besoins.
Autre inconvénient des listes de propriétés par rapport
aux tableaux classiques que l'on trouve habituellement dans les langages, c'est
qu'elles sont limitées à des ensembles de 2 valeurs : elles sont
à deux dimensions. On peut dépasser cette limitation en utilisant
des listes de listes (les éléments de la liste principale sont
eux-mêmes des listes, linéaires ou de propriétés),
mais alors le modèle risque de devenir rapidement complexe et exigera
une grande attention pour ne pas se tromper dans les manipulations.
Pour simplifier, les listes de propriétés conviennent bien si
chaque propriété est unique dans la liste. Les choses se compliquent
sensiblement si il existe des doublons dans les propriétés.
Enfin, si certaines commandes
de liste sont applicables aux deux types, linéaire et de propriété,
chacun de ces deux types dispose aussi d'un jeu d'instructions qui lui est propre.
Voici quel va être
le principe du traitement de dédoublonnage (mais on peut en trouver d'autres),
et pour commencer, sans comptage :
- nous allons regarder dans la liste des mots, les deux premiers mots,
- deux cas sont possibles : ces deux éléments sont identiques,
ou ils sont différents.
- s'ils sont identiques, cela veut dire que nous sommes en présence d'un doublon. On peut alors supprimer le premier élément de la liste. L'élément qui était le deuxième de la liste est alors devenu le premier.
On regarde donc à nouveau les éléments de rang 1 et 2. Deux cas sont possibles, etc...- s'ils sont différents, cela veut dire que le premier élément est unique (puisque la liste est triée) et qu'il doit donc être conservé.
Là, deux manières de faire s'offrent à nous :
- soit on utilise un pointeur qui augmentera d'une unité lorsqu'on trouve un mot unique, et qui permettra donc, pour le pas suivant du traitement, de regarder les éléments 2 et 3 (puisque le premier est unique, on le laisse dans la liste et on passe à l'examen des mots suivants), puis 3 et 4, puis 4 et 5, et ainsi de suite à chaque fois qu'on trouve un mot unique,
- mais le plus simple est de se doter d'une deuxième liste, que nous pourrons appeler lstMotsComptes (c'est le nom que j'ai adopté dans l'exercice, mais le comptage ne sera vu qu'un peu plus bas), qui sera initialisée comme une liste vide avant de commencer le traitement. Lorsque les éléments 1 et 2 de la liste source seront différents, et que donc le premier élément est unique, on ajoutera cet élément à lstMotsComptes, et on le supprimera de la liste source. De cette manière, dans la liste source, on regardera toujours, jusqu'au bout du traitement, les éléments 1 et 2.
L'ensemble du
traitement se fera dans une boucle de répétition, jusqu'à
ce que la liste source ne comprenne plus qu'un seul élément :
puisqu'on détruit à chaque fois le premier élément,
on aura forcément épuisé la liste source quand on aura
détruit un à un tous les éléments de la liste source,
sauf le dernier. Ce traitement sera totalement identique à chaque pas
jusqu'à ce qu'il ne reste que deux éléments dans la liste.
Lorsqu'il ne reste que deux éléments, on ne peut plus enlever
le premier et regarder les deux suivants puisqu'en enlevant le premier, il ne
reste plus qu'un seul élément dans la liste.
Le traitement à faire alors, lorsqu'il ne reste que deux éléments
dans la liste, est de les comparer :
- s'ils sont identiques, on détruit le premier et on ajoute le dernier
qui reste à la listeDesMotsComptes (et alors le count
de la listeDesMotsComptes est égal à 1 donc on sort de
la boucle de répétition), et
- s'ils sont différents, on ajoute ces deux derniers éléments
à la liste listeDesMotsComptes, et le traitement est terminé.
Toutefois, pour pouvoir sortir de la boucle de répétition (qui
s'exécute tant que la liste source contient plus d'un élément),
il faut détruire le premier ces deux derniers éléments
de la liste source, de manière à mettre son count() à
1.
Ce qui pourra s'écrire
en pseudo-code (du fait qu'il y a beaucoup d'imbrications, j'ai mis les structures
de code qui se correspondent dans une couleur différente) :
NB : je donne tout de suite
le nom de chercheroccurrences() à la méthode, puisque c'est ce
à quoi elle va servir quand nous l'aurons complétée et
terminée.
on
chercheoccurrences me
trier
lstMots
lstMotsComptes
= []
repéter
tant que lstMots contient plus d'1 élément
![]()
mot1
= le premier mot de lstMots
![]()
mot2
= le deuxième mot de lstMots
![]()
si
le nombre d'éléments de lstMots > 2 alors
![]()
![]()
si
mot1 = mot2 alors
![]()
![]()
![]()
détruire
le premier mot de lstMots
![]()
![]()
sinon
![]()
![]()
![]()
ajouter
mot1 à listeDesMotsComptes
![]()
![]()
![]()
détruire
le premier mot de lstMots
![]()
![]()
fin
si
![]()
sinon
si le nombre d'éléments de lstMots = 2 alors
![]()
![]()
si
mot1 = mot2 alors
![]()
![]()
![]()
détruire
le premier mot de lstMots
![]()
![]()
![]()
ajouter
mot2 à lstMotsComptes
![]()
![]()
sinon
![]()
![]()
![]()
ajouter
mot1 à lstMotsComptes
![]()
![]()
![]()
ajouter
mot2 à lstMotsComptes
![]()
![]()
![]()
détruire
le premier mot de lstMots
![]()
![]()
fin
si
![]()
fin
si
fin
répéter
end
Ce qui pourra se coder (en reprenant les mêmes couleurs pour distinguer les structures du code) :
on
chercheoccurrences me
lstMots.sort()
lstMotsComptes
= []
repeat
while lstMots.count() > 1
![]()
mot1
= lstMots[1]
![]()
mot2
= lstMots[2]
![]()
if
lstMots.count() > 2 then
![]()
![]()
if
mot1 = mot2 then
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
else
![]()
![]()
![]()
lstMotsComptes.add(mot1)
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
end
if
![]()
else
if lstMots.count() = 2 then
![]()
![]()
if
mot1 = mot2 then
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
![]()
lstMotsComptes.add(mot2)
![]()
![]()
else
![]()
![]()
![]()
lstMotsComptes.add(mot1)
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
![]()
lstMotsComptes.add(mot2)
![]()
![]()
end
if
![]()
end
if
end
repeat
end
Avant de commencer
le traitement de dédoublonnage ci-dessus, il aurait normalement fallu
reconstituer la liste lstMots. En effet, dans l'étape précédente,
cette liste lstMots n'était qu'une simple variable locale, qui
servait jusqu'à la fin du script faireStats() pour élaborer
le message de résultat. Nous n'utilisions plus lstMots ailleurs,
et il n'y avait donc aucune raison de la faire perdurer.
Pour en disposer à nouveau ici, nous aurions pu réécrire
le traitement de construction de lstMots, et nous aurions donc été
conduits à écrire deux fois la même partie de code : une
fois dans faireStats(), et une autre fois dans chercheoccurrences().
Ce qui est mauvais, nous le savons.
Répétons-le une fois de plus, dès que l'on s'aperçoit
qu'on est train d'écrire du code que l'on a déjà écrit
ailleurs, alors il faut repenser la conception pour éviter cette répétition.
Dans le cas présent, nous pourrions sortir le traitement de constitution
de lstMots, pour en faire une fonction, laquelle serait appelée
depuis faireStats() là où nécessaire, et qui serait
à nouveau appelée depuis chercheoccurrences().
Ce serait déjà mieux, mais il y a plus simple et plus optimisé.
Puisque nous devons passer par faireStats() (et donc fabriquer la liste
lstMots) AVANT de pouvoir accéder à
la recherche d'occurrences (cahier des charges), alors il nous suffit de stocker
lstMots dans une entité qui cette fois, perdure, c'est à
dire que nous allons en faire une propriété du bouton.
Pour écrire ce début de chercheoccurrences(), et pour
qu'il fonctionne, nous aurons donc rajouté lstMots sur la ligne
de déclaration des propriétés, et nous aurons initialisé
cette propriété comme une liste vide dans le beginSprite
(je ne reproduis pas les lignes correspondantes ici : ce sont des choses que
nous avons déjà faites, et on trouvera le script modifié
dans le .dir téléchargeable).
Cela réduit le nombre de lignes de notre code. En outre, ce code est optimisé : dans le cas d'une fonction appelée, le calcul serait refait à chaque fois que l'on a besoin de lstMots. Dans la solution ici adoptée, le calcul est fait une fois pour toutes. Le recours à lstMots occupe de la mémoire, mais son appel ne sollicite plus le processeur.
A noter que bien que le traitement de dédoublonnage détruise la liste lstMots, il n'est pas nécessaire d'en faire une sauvegarde avant le traitement de dédoublonnage. En effet, dans les utilisations ultérieures du bouton, il faudra repasser par les statistiques du texte, donc la liste lstMots sera recalculée et reconstituée avant qu'on en ait besoin à nouveau.
Nous reste à
implémenter le comptage de chaque occurrence, et à élaborer
le message de résultat.
C'est assez simple, mais à cette occasion, nous allons découvrir
une première instruction propre aux listes de propriétés
: celle qui permet d'ajouter, en une seule fois, une propriété
et sa valeur associée :
cette instruction s'écrit uneListeDePropriétés.addProp(unePropriété,
saValeur)
Nous allons aussi devoir modifier la déclaration de la variable lstMotsComptes.
Nous l'avions déclarée comme une liste linéaire, il va
falloir mainenant la déclarer comme une ligne de propriétés
: au lieu d'écrire lstMotsComptes = [], nous allons écrire
lstMotsComptes = [:].
Ensuite, il nous
faudra un compteur. Initialisé à 0 avant de rentrer dans la boucle
de dédoublonnage, ce compteur sera incrémenté de 1 à
chaque fois que l'on trouve une occurrence de doublon.
En revanche, lorsque nous aurons affaire à un mot unique, soit que nous
ayons épuisé toutes les occurrences d'un mot, soit que l'on ait
effectivement affaire à un mot unique, nous devrons réinitialiser
le compteur à 0 pour pouvoir recommencer le comptage sur le mot suivant.
Les opérations sur le compteur seront toutefois un peu différentes
lorsqu'il ne reste plus que deux éléments dans lstMots
: si les deux mots qui restent sont identiques, alors c'est un doublon, donc
nous avons fini le comptage, et en même temps que nous n'ajoutons en propriété
qu'un seul exemplaire de ce mot à lstMotsComptes, il faut incrémenter
le compteur de 2 avant de l'inclure dans lstMotsComptes
comme valeur associée à ce mot, puisque nous
sommes en présence de deux de ses occurrences.
Si en revanche les deux mots sont différents, alors on ajoute le premier
à lstMotsComptes, on incrémente son compteur de 1, et
on l'inscrit dans lstMotsComptes comme valeur de ce premier mot. Puis
nous ajoutons le deuxième mot restant, avec un valeur de 1, puisqu'il
reste seul et donc unique.
Pour l'élaboration du message, on opére de la même manière que pour l'affichage des statisques, sauf que nous devons cette fois afficher le contenu entier d'une liste. On initialise donc une variable locale contenant l'en-tête du message ("ce texte contient tant de mots différents, classés par ordre alphabétique"). On rajoute ensuite, à l'aide d'une boucle de répétition balayant la liste entière, autant de lignes (sans oublier le saut) qu'il y a de mots différents, chaque ligne étant composée du mot, de quelques points de suite, et de la fréquence de ce mot.
Nous rencontrons
cette fois un problème de présentation. En effet, à moins
d'utiliser une police à chasse fixe (non proportionnelle) dans laquelle
la largeur d'un mot est déterminée par son nombre de caractères,
il n'est pas possible de savoir quelle est la largeur, en terme de place physique,
en nombre de pixels par exemple, qu'occupe un mot. On ne peut donc pas aligner
verticalement les fréquences.
A noter d'ailleurs que les tabulations ne sont pas honorées dans l'affichage
d'un champ texte si on les intercale à l'aide d'un script (avec la constante
TAB ou par la conversion numToChar(9) ). Elles semblent prises
en compte lorsqu'on les tape au clavier directement en éditant l'acteur
champ, mais à l'affichage sur la scène, Director les remplace
par des carrés, signe jocker utilisé pour matérialiser
les caractères non-imprimables.
Quoi qu'il en soit, si on les tape au clavier, elles ne sont de toutes façons
pas calées sur des taquets fixes, mais se contentent d'insérer
un espace large : à l'arrivée, on trouve le même décalage
introduit par la police à chasse variable.
On peut atténuer le phénomène en introduisant un calcul
du nombre de points de suite en fonction du nombre de caractères de chaque
mot (ce qui a été fait dans l'exercice), mais un peu inutilement
du point de vue de la présentation du résultat.
Pour cela il nous faut calculer la longueur du mot le plus long dans lstMotsComptes
au moyen d'une boucle de répétition. La longueur du mot le plus
long de la liste, appelée longueurPlusLongMot
est initialisée à 0 avant la boucle.
Ensuite, on parcourt la liste, on regarde la longueur de chaque mot. Si celle-ci
est supérieure à la valeur actuelle de longueurPlusLongMot,
alors, longueurPlusLongMot prend la valeur du nombre de caractères
du nouveau mot, qui est plus long que tous ceux qu'on avait trouvés jusque
là. A la fin de la boucle, longueurPlusLongMot contient le nombre
de caractères du mot le plus long de la liste.
Puis, dans la boucle d'élaboration du message, on calculera un nombre
de points de suite, qui ne soit pas inférieur à 5, et qui d'autre
part tient également compte du fait que la fréquence est un nombre
à un chiffre ou à deux chiffres.
Cela ne donne pas un résultat très satisfaisant, mais on pourra
faire l'essai d'attribuer à ce champ un police à chasse fixe,
et on constatra alors que ce calcul fonctionne bien.
Ce qui va nous donner, en
pseudo-code (seuls les ajouts par rapport à la version précédente
sont écrits en pseudo-code) :
on
chercheoccurrences me
lstMots.sort()
lstMotsComptes
= [:]
compteur
= 0
repeat
while lstMots.count() > 1
![]()
mot1
= lstMots[1]
![]()
mot2
= lstMots[2]
![]()
if
lstMots.count() > 2 then
![]()
![]()
if
mot1 = mot2 then
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
![]()
incrémenter
le compteur de 1
![]()
![]()
else
![]()
![]()
![]()
incrémenter
le compteur de 1
![]()
![]()
![]()
ajouter
à la lstMotsComptes mot1 et la valeur du compteur
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
![]()
réinitialiser
le compteur à 0
![]()
![]()
end
if
![]()
else
if lstMots.count() = 2 then
![]()
![]()
if
mot1 = mot2 then
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
![]()
incrémenter
le compteur de 2
![]()
![]()
![]()
ajouter
à la lstMotsComptes mot2 et la valeur du compteur
![]()
![]()
else
![]()
![]()
![]()
incrémenter
le compteur de 1
![]()
![]()
![]()
ajouter
à lstMotsComptes mot1 et la valeur du compteur
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
![]()
ajouter
à lstMotsComptes mot2 et la valeur 1
![]()
![]()
end
if
![]()
end
if
end
repeat
longueurPlusLongMot
= 0
répéter
avec un compteur i de 1 jusqu'au nombre d'éléments de lstMotsComptes
![]()
si
le nombre de caractères du mot (propriété) de rang i dans
lstMotsComptes est plus long que longueurPlusLongMot alors
![]()
![]()
longueurPlusLongMot
= le nombre de caractères du mot de rang i dans lstMotsComptes
![]()
fin
si
fin
répéter
leMessage
= "Ce texte contient" auquel on ajoute le nombre d'éléments
de lstMotsComptes auquel on ajoute "mots différents, classés
par ordre alphabétique." auquel on ajoute deux retour chariot (pour
laisser un interligne)
répéter
avec un compteur i de 1 jusqu'au nombre d'éléments de lstMotsComptes
![]()
leMot
= le mot (propriété) de rang i dans lstMotsComptes
![]()
laFrequence
= la valeur associée à leMot dans lstMotsComptes
![]()
si
laFrequence est inférieure à 10 (donc un seul caractère)
![]()
![]()
nombre
de points de suite à insérer = 6 + (longueurPlusLongMot - la longuer
de leMot)
![]()
sinon
![]()
![]()
nombre
de points de suite à insérer = 5 + (longueurPlusLongMot - la longuer
de leMot)
![]()
fin
si
![]()
Les
points de suite = une chaîne vide
![]()
répéter
avec un compteur j de 1 jusqu'à nombre de points de suite à insérer
![]()
![]()
les
points de suite = les points de suite auquel on ajoute un point supplémentaire
![]()
fin
répéter
![]()
leMessage
= leMessage auquel on ajoute un tiret et un espace auquel on ajoute leMot auquel
on ajoute le nombre de points de suite à insérer auquel on ajoute
laFrequence auquel on ajoute un saut de ligne
fin
répéter
fixer
la propriété text de l'acteur champ resultat à leMessage
end
Nous allons maintenant
pourvoir coder cette méthode chercheoccurrences() en entier.
Nous avons déjà vu que pour ajouter un couple propriété/valeur
à une liste de propriétés, on utilisait une instruction
uneListeDepropriétés.addProp(unePropriété, saValeur).
Nous allons également avoir besoin, maintenant, de récupérer
des couples propriété/valeur dans notre liste de propriétés.
Pour les listes linéaires, c'était facile : il suffisait d'utiliser
les crochets : telleValeurDeTelRang = uneListeLinéaire[telRang].
Pour les listes de propriétés, c'est un peu plus compliqué
: il existe une instruction qui permet de récupérer la propriété
qui se trouve à tel rang dans la liste (l'équivalent des crochets
pour les listes linéaires). Cette instruction est : uneListeDePropriétés.getPropAt(leRang)
Ensuite, une fois que l'on connait la propriété, on peut récupérer
la valeur qui lui est associée, à l'aide d'une instruction qui
est : uneListeDePropriétés.getProp(unePropriété)
Mais on NE PEUT PAS récupérer seulement une valeur
en indiquant son rang dans la liste de propriétés. On ne peut
pas non plus récupérer d'un seul coup une propriété
et sa valeur en indiquant le rang du couple.
Ceci veut dire
:
- qu'on peut toujours récupérer toutes les propriétés
d'une liste de propriétés, même s'il existe des propriétés
de même nom, puisqu'on accède à ces propriétés
par leur rang dans la liste,
- que, si il existe plusieurs propriétés de même nom dans
la liste, on ne pourra accéder qu'à la valeur de la première
propriété dans la liste d'un nom donné, et jamais aux valeurs
associées aux propriétés suivantes (d'un rang plus élevé
dans la liste) de même nom dans la liste. En effet, l'instruction getProp
renvoie la valeur de la première propriété rencontrée,
et ignore les éventuelles suivantes.
C'est la raison pour laquelle j'avais mentionné, en introduction aux
listes de propriétés, que celles-ci pouvaient convenir à
condition que chaque propriété soit unique dans la liste.
Donc, voici notre méthode chercheoccurrences() une fois codée :
on chercheoccurrences me
lstMots.sort()
lstMotsComptes
= [:]
compteur
= 0
repeat
while lstMots.count() > 1
![]()
mot1
= lstMots[1]
![]()
mot2
= lstMots[2]
![]()
if
lstMots.count() > 2 then
![]()
![]()
if
mot1 = mot2 then
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
![]()
compteur
= compteur + 1
![]()
![]()
else
![]()
![]()
![]()
compteur
= compteur + 1
![]()
![]()
![]()
lstMotsComptes.addProp(mot1,
compteur)
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
![]()
compteur
= 0
![]()
![]()
end
if
![]()
else
if lstMots.count() = 2 then
![]()
![]()
if
mot1 = mot2 then
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
![]()
compteur
= compteur + 2
![]()
![]()
![]()
lstMotsComptes.addProp(mot2,
compteur)
![]()
![]()
else
![]()
![]()
![]()
compteur
= compteur + 1
![]()
![]()
![]()
lstMotsComptes.addProp(mot1,
compteur)
![]()
![]()
![]()
lstMots.deleteAt(1)
![]()
![]()
![]()
lstMotsComptes.addProp(mot2,
1)
![]()
![]()
end
if
![]()
end
if
end
repeat
longueurPlusLongMot
= 0
repeat
with i = 1 to lstMotsComptes.count
![]()
if
lstMotsComptes.getPropAt(i).length > longueurPlusLongMot then
![]()
![]()
longueurPlusLongMot
= lstMotsComptes.getPropAt(i).length
![]()
end
if
end
repeat
leMessage
= "Ce texte contient"&&lstMotsComptes.count()&&"mots
différents, classés par ordre alphabétique."&return&return
repeat
with i = 1 to lstMotsComptes.count()
![]()
leMot
= lstMotsComptes.getPropAt(i)
![]()
laFrequence
= lstMotsComptes.getProp(leMot)
![]()
if
laFrequence < 10 then
![]()
![]()
nbPoints
= 6 + (longueurPlusLongMot - lstMotsComptes.getPropAt(i).length)
![]()
else
![]()
![]()
nbPoints
= 5 + (longueurPlusLongMot - lstMotsComptes.getPropAt(i).length)
![]()
end
if
![]()
lesPoints
= ""
![]()
repeat
with j = 1 to nbPoints
![]()
![]()
lesPoints
= lesPoints&"."
![]()
end
repeat
![]()
leMessage
= leMessage&"- "&leMot&lesPoints&laFrequence&RETURN
end
repeat
member("resultat").text
= leMessage
end
Nous avons maintenant
terminé cette première partie de l'analyse des occurrences.
Dans l'étape suivante, nous rajouterons les choix de critère et
d'ordre de tri de ces occurrences.
(*) A noter qu'il faut prendre en compte ce système de classement lorsqu'on est amené à traiter des séries de fichiers (comme une exportation image par image d'une vidéo par exemple), et qu'il faut les nommer. En effet, on les nomme en général à l'aide d'un numéro d'ordre, précédé éventuellement d'un préfixe ou suivi d'un suffixe constant.
Si on nomme ces
fichiers "1", "2", "3", "4", "5",
"6", "7", "8", "9", "10",
"11", "12"... ,le système d'exploitation les présentera
triés par ordre alphabétique (et c'est aussi l'ordre de tri que
reprendra Director dans la distribution au moment d'importer ces fichiers),
puisqu'un nom de fichier est une chaîne.
Donc les fichiers seront dans l'ordre suivant :
"1", "10", ...ici toutes les dizaines, puis toutes les centaines...,
"2", "20", ici toutes les vingtaines et les deux-centaines...,
"3", "30", "31", "32", ... ici toutes
les trentaines, puis les trois-centaines...
Si on a des milliers, le premier millier sera à la suite des centaines,
le deuxième millier à la suite des deux-centaines, etc.
Les fichiers ne seront donc pas dans l'ordre où ils doivent se présenter pour la séquence, et on perdra beaucoup de temps à les retrier et les renommer à la main, comme on le voit faire souvent !
Pour éviter ce désagrément, il suffit de regarder le nombre total de fichiers que l'on a à traiter et d'en déduire le nombre de caractères que doit comporter le nom de fichier. Si on ne dépasse pas la centaine (maximum 99), alors deux caractères suffiront, si on ne dépasse pas le millier (maximum 999) alors il faudra trois caractères, et ainsi de suite.
Ensuite, il suffira
de faire précéder le numéro de fichier par le nombre de
zéros nécessaires pour compléter le nombre de caractères
utilisés. Par exemple, si on est en dessous du millier, et qu'on attribue
les noms sur trois caractères, la numérotation commencera ainsi
:
"001", "002", "003", "004", "005",
"006", "007", "009", "010", "011"
... etc. Et ils resteront dans l'ordre.
Si on utilise un préfixe ou un suffixe dans le nom de fichier, celui-ci
n'interviendra pas sur l'ordre alphabétique puisqu'il s'agit d'une chaîne
constante (la même pour tous les fichiers). On dit aussi une expression
régulière (par exemple "telleSequence_001", "telleSequence_002",
"telleSequence_003", ...).
Toutefois, si on s'est trompé dans le nommage au moment d'une exportation et qu'on a un grand nombre de fichiers à renommer, rien n'est perdu si on pense au service que peut rendre le code : on pourra se faire un petit script utilitaire qui renommera les fichiers ou acteurs correctement et qui replacera ceux qui doivent l'être au bon endroit. Il suffira pour cela de faire un hachage de chacun des noms de fichiers pour en eXtraire, sous forme de valeur numérique, son numéro réel, et de reconcaténer ce numéro avec des caractères qui vont bien (notamment l'insertion de zéros, pour recréer un nom de fichiers correct. On changera ensuite le nom des acteurs en modifiant leur propriété name.
| Retour à la sixième étape |