J'ai récemment commencé à écrire un jeu dans lequel vous construisez des mots à l'aide de tuiles de lettres. Pour créer le jeu, j'avais besoin de connaître la fréquence des lettres dans les mots réguliers de la langue anglaise, afin de pouvoir présenter un ensemble utile de tuiles de lettres. La fréquence des lettres est discutée à divers endroits, y compris sur Wikipédia, mais je voulais calculer moi-même la fréquence des lettres.
Linux fournit une liste de mots dans le /usr/share/dict/words
fichier, j'ai donc déjà une liste de mots susceptibles d'être utilisés. Les words
le fichier contient beaucoup de mots que je veux, mais quelques-uns que je ne veux pas. Je voulais une liste de tous les mots qui n'étaient pas des mots composés (pas de traits d'union ni d'espaces) ou des noms propres (pas de lettres majuscules). Pour obtenir cette liste, je peux exécuter le grep
commande pour extraire uniquement les lignes composées uniquement de lettres minuscules :
$ grep '^[a-z]*$' /usr/share/dict/words
Cette expression régulière demande grep
pour faire correspondre des modèles qui ne sont que des lettres minuscules. Les caractères ^
et $
dans le motif représentent respectivement le début et la fin de la ligne. Le [a-z]
le groupement correspondra uniquement aux lettres minuscules a à z .
Voici un exemple rapide du résultat :
$ grep '^[a-z]*$' /usr/share/dict/words | tête
a
aa
aaa
aah
aahed
aahing
aahs
aal
aalii
aaliis
Plus de ressources Linux
- Aide-mémoire des commandes Linux
- Aide-mémoire des commandes Linux avancées
- Cours en ligne gratuit :Présentation technique de RHEL
- Aide-mémoire sur le réseau Linux
- Aide-mémoire SELinux
- Aide-mémoire sur les commandes courantes de Linux
- Que sont les conteneurs Linux ?
- Nos derniers articles Linux
Et oui, ce sont tous des mots valables. Par exemple, "aahed" est l'exclamation au passé de "aah", comme dans la relaxation. Et un "aalii" est un arbuste tropical touffu.
Il ne me reste plus qu'à écrire un gawk
script pour faire le travail de comptage des lettres dans chaque mot, puis imprimer la fréquence relative de chaque lettre qu'il trouve.
Compter les lettres
Une façon de compter les lettres dans gawk
consiste à parcourir chaque caractère de chaque ligne d'entrée et à compter les occurrences de chaque lettre a à z . Le substr
renverra une sous-chaîne d'une longueur donnée, telle qu'une seule lettre, à partir d'une chaîne plus grande. Par exemple, cet exemple de code évaluera chaque caractère c
depuis l'entrée :
{
longueur =longueur($0); for (i =1; i <=len; i++) {
c =substr($0, i, 1);
}
}
Si je commence par une chaîne globale LETTERS
qui contient l'alphabet, je peux utiliser l'index
fonction pour trouver l'emplacement d'une seule lettre dans l'alphabet. Je vais développer le gawk
exemple de code pour évaluer uniquement les lettres a à z dans l'entrée :
BEGIN { LETTERS ="abcdefghijklmnopqrstuvwxyz" }
{
len =length($0); for (i =1; i <=len; i++) {
c =substr($0, i, 1);
ltr =index(LETTERS, c);
}
}
Notez que la fonction index renvoie la première occurrence de la lettre de LETTERS
chaîne, commençant par 1 à la première lettre, ou zéro s'il n'est pas trouvé. Si j'ai un tableau de 26 éléments, je peux utiliser le tableau pour compter les occurrences de chaque lettre. Je vais ajouter ceci à mon exemple de code pour incrémenter (en utilisant ++
) le nombre de chaque lettre tel qu'il apparaît dans l'entrée :
BEGIN { LETTERS ="abcdefghijklmnopqrstuvwxyz" }
{
len =length($0); for (i =1; i <=len; i++) {
c =substr($0, i, 1);
ltr =index(LETTERS, c);
if (ltr> 0) {
++count[ltr];
}
}
}
Fréquence relative d'impression
Après le gawk
le script compte toutes les lettres, je veux imprimer la fréquence de chaque lettre qu'il trouve. Je ne suis pas intéressé par le nombre total de chaque lettre de l'entrée, mais plutôt par la fréquence relative de chaque lettre. La fréquence relative met à l'échelle les comptes de sorte que la lettre avec le moins d'occurrences (comme la lettre q ) est défini sur 1, et les autres lettres sont relatives à cela.
Je vais commencer par le décompte pour la lettre a , puis comparez cette valeur aux décomptes pour chacune des autres lettres b à z :
END {
min =count[1] ; for (ltr =2; ltr <=26; ltr++) {
if (count[ltr]min =count[ltr];
}
}
}
A la fin de cette boucle, la variable min
contient le nombre minimum pour n'importe quelle lettre. Je peux l'utiliser pour fournir une échelle pour les comptes afin d'imprimer la fréquence relative de chaque lettre. Par exemple, si la lettre avec l'occurrence la plus basse est q , puis min
sera égal au q compter.
Ensuite, je parcours chaque lettre et je l'imprime avec sa fréquence relative. Je divise chaque compte par min
pour imprimer la fréquence relative, ce qui signifie que la lettre avec le nombre le plus bas sera imprimée avec une fréquence relative de 1. Si une autre lettre apparaît deux fois plus souvent que le nombre le plus bas, cette lettre aura une fréquence relative de 2. Je suis seulement intéressé par les valeurs entières ici, donc 2.1 et 2.9 sont les mêmes que 2 pour mes besoins :
END {
min =count[1] ; for (ltr =2; ltr <=26; ltr++) {
if (count[ltr]min =count[ltr];
}
}
for (ltr =1; ltr <=26; ltr++) {
print substr(LETTERS, ltr, 1), int(count[ltr] / min);
}
}Tout mettre ensemble
Maintenant j'ai un
gawk
script qui peut compter la fréquence relative des lettres dans son entrée :#!/usr/bin/gawk -f
# compte uniquement a-z, ignore A-Z et tout autre caractère
BEGIN { LETTERS ="abcdefghijklmnopqrstuvwxyz" }
{
longueur =longueur($0); for (i =1; i <=len; i++) {
c =substr($0, i, 1);
ltr =index(LETTERS, c);
if (ltr> 0) {
++count[ltr] ;
}
}
}
# affiche la fréquence relative de chaque lettre
FIN {
min =count[1] ; for (ltr =2; ltr <=26; ltr++) {
if (count[ltr]min =count[ltr];
}
}
for (ltr =1; ltr <=26; ltr++) {
print substr(LETTERS, ltr, 1), int(count[ltr] / min);
}
}Je vais enregistrer cela dans un fichier appelé
letter-freq.awk
afin que je puisse l'utiliser plus facilement depuis la ligne de commande.Si vous préférez, vous pouvez également utiliser
chmod +x
pour rendre le fichier exécutable par lui-même. Le#!/usr/bin/gawk -f
sur la première ligne signifie que Linux l'exécutera en tant que script en utilisant le/usr/bin/gawk
programme. Et parce que legawk
la ligne de commande utilise-f
pour indiquer quel fichier il doit utiliser comme script, vous avez besoin de ce-f
suspendu de sorte que l'exécution deletter-freq.awk
au niveau du shell sera correctement interprété comme exécutant/usr/bin/gawk -f letter-freq.awk
à la place.Je peux tester le script avec quelques entrées simples. Par exemple, si j'introduis l'alphabet dans mon
gawk
script, chaque lettre doit avoir une fréquence relative de 1 :$ echo abcdefghijklmnopqrstuvwxyz | gawk -f lettre-freq.awk
a 1
b 1
c 1
d 1
e 1
f 1
g 1
h 1
i 1
j 1
k 1
l 1
m 1
n 1
o 1
p 1
q 1
r 1
s 1
t 1
u 1
v 1
w 1
x 1
y 1
z 1Répétition de cet exemple mais en ajoutant une instance supplémentaire de la lettre e imprimera la lettre e avec une fréquence relative de 2 et toutes les autres lettres comme 1 :
$ echo abcdeefghijklmnopqrstuvwxyz | gawk -f lettre-freq.awk
a 1
b 1
c 1
d 1
e 2
f 1
g 1
h 1
i 1
j 1
k 1
l 1
m 1
n 1
o 1
p 1
q 1
r 1
s 1
t 1
u 1
v 1
w 1
x 1
y 1
z 1Et maintenant, je peux faire le grand pas ! Je vais utiliser le
grep
commande avec le/usr/share/dict/words
fichier et identifiez la fréquence des lettres pour tous les mots entièrement écrits en minuscules :$ grep '^[a-z]*$' /usr/share/dict/words | gawk -f lettre-freq.awk
a 53
b 12
c 28
d 21
e 72
f 7
g 15
h 17
i 58
j 1
k 5
l 36
m 19
n 47
o 47
p 21
q 1
r 46
s 48
t 44
u 25
v 6
w 4
x 1
y 13
z 2De tous les mots en minuscules dans le
/usr/share/dict/words
fichier, les lettres j , q , et x surviennent le moins fréquemment. La lettre z est aussi assez rare. Sans surprise, la lettre e est le plus fréquemment utilisé.