Di recente ho iniziato a scrivere un gioco in cui costruisci parole usando le tessere delle lettere. Per creare il gioco, dovevo conoscere la frequenza delle lettere sulle parole normali della lingua inglese, in modo da poter presentare un utile set di tessere delle lettere. La frequenza delle lettere è discussa in vari luoghi, incluso su Wikipedia, ma volevo calcolare io stesso la frequenza delle lettere.
Linux fornisce un elenco di parole in /usr/share/dict/words
file, quindi ho già un elenco di parole probabili da usare. Le words
file contiene molte parole che voglio, ma alcune che non voglio. Volevo un elenco di tutte le parole che non erano parole composte (senza trattini o spazi) o nomi propri (senza lettere maiuscole). Per ottenere quell'elenco, posso eseguire grep
comando per estrarre solo le righe composte esclusivamente da lettere minuscole:
$ grep '^[a-z]*$' /usr/share/dict/words
Questa espressione regolare chiede grep
per abbinare i modelli che sono solo lettere minuscole. I caratteri ^
e $
nel modello rappresentano rispettivamente l'inizio e la fine della linea. Il [a-z]
il raggruppamento corrisponderà solo alle lettere minuscole a a z .
Ecco un rapido esempio dell'output:
$ grep '^[a-z]*$' /usr/share/dict/words | head
a
aa
aaa
aah
aahed
aahing
aahs
aal
aalii
aaliis
Più risorse Linux
- Comandi Linux cheat sheet
- Cheat sheet sui comandi avanzati di Linux
- Corso online gratuito:Panoramica tecnica RHEL
- Cheat sheet della rete Linux
- Cheat sheet di SELinux
- Cheat sheet dei comandi comuni di Linux
- Cosa sono i container Linux?
- I nostri ultimi articoli su Linux
E sì, quelle sono tutte parole valide. Ad esempio, "aahed" è l'esclamazione al passato di "aah", come in relax. E un "aalii" è un cespuglioso arbusto tropicale.
Ora devo solo scrivere un gawk
script per eseguire il lavoro di conteggio delle lettere in ogni parola, quindi stampare la frequenza relativa di ogni lettera che trova.
Conteggio delle lettere
Un modo per contare le lettere in gawk
consiste nell'iterare ogni carattere in ogni riga di input e contare le occorrenze di ogni lettera a a z . Il substr
funzione restituirà una sottostringa di una determinata lunghezza, ad esempio una singola lettera, da una stringa più grande. Ad esempio, questo esempio di codice valuterà ogni carattere c
dall'input:
{
len = length($0); for (i = 1; i <= len; i++) {
c = substr($0, i, 1);
}
}
Se inizio con una stringa globale LETTERS
che contiene l'alfabeto, posso usare l'index
funzione per trovare la posizione di una singola lettera dell'alfabeto. Espanderò il gawk
esempio di codice per valutare solo le lettere a a z nell'input:
BEGIN { LETTERS = "abcdefghijklmnopqrstuvwxyz" }
{
len = length($0); for (i = 1; i <= len; i++) {
c = substr($0, i, 1);
ltr = index(LETTERS, c);
}
}
Nota che la funzione di indice restituisce la prima occorrenza della lettera da LETTERS
stringa, che inizia con 1 alla prima lettera, o zero se non trovata. Se ho un array lungo 26 elementi, posso usare l'array per contare le occorrenze di ogni lettera. Lo aggiungerò al mio esempio di codice per incrementare (usando ++
) il conteggio di ogni lettera così come appare nell'input:
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];
}
}
}
Frequenza relativa di stampa
Dopo il gawk
lo script conta tutte le lettere, voglio stampare la frequenza di ogni lettera che trova. Non mi interessa il numero totale di ciascuna lettera dall'input, ma piuttosto la frequenza relativa di ogni lettera. La frequenza relativa ridimensiona i conteggi in modo che la lettera con il minor numero di occorrenze (come la lettera q ) è impostato su 1 e altre lettere sono relative a quello.
Inizierò con il conteggio della lettera a , quindi confronta quel valore con i conteggi per ciascuna delle altre lettere b a z :
END {
min = count[1]; for (ltr = 2; ltr <= 26; ltr++) {
if (count[ltr] < min) {
min = count[ltr];
}
}
}
Alla fine di quel ciclo, la variabile min
contiene il conteggio minimo per ogni lettera. Posso usarlo per fornire una scala per i conteggi per stampare la frequenza relativa di ogni lettera. Ad esempio, se la lettera con l'occorrenza più bassa è q , quindi min
sarà uguale a q contare.
Quindi apro ogni lettera e la stampo con la relativa frequenza. Divido ogni conteggio per min
per stampare la frequenza relativa, il che significa che la lettera con il conteggio più basso verrà stampata con una frequenza relativa di 1. Se un'altra lettera appare due volte più spesso del conteggio più basso, quella lettera avrà una frequenza relativa di 2. Sono solo interessato a valori interi qui, quindi 2.1 e 2.9 sono gli stessi di 2 per i miei scopi:
END {
min = count[1]; for (ltr = 2; ltr <= 26; ltr++) {
if (count[ltr] < min) {
min = count[ltr];
}
}
for (ltr = 1; ltr <= 26; ltr++) {
print substr(LETTERS, ltr, 1), int(count[ltr] / min);
}
}
Mettere tutto insieme
Ora ho un gawk
script che può contare la frequenza relativa delle lettere nel suo input:
#!/usr/bin/gawk -f
# only count a-z, ignore A-Z and any other characters
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];
}
}
}
# print relative frequency of each letter
END {
min = count[1]; for (ltr = 2; ltr <= 26; ltr++) {
if (count[ltr] < min) {
min = count[ltr];
}
}
for (ltr = 1; ltr <= 26; ltr++) {
print substr(LETTERS, ltr, 1), int(count[ltr] / min);
}
}
Lo salverò in un file chiamato letter-freq.awk
in modo da poterlo utilizzare più facilmente dalla riga di comando.
Se preferisci, puoi anche usare chmod +x
per rendere il file eseguibile da solo. Il #!/usr/bin/gawk -f
sulla prima riga significa che Linux lo eseguirà come script utilizzando /usr/bin/gawk
programma. E perché il gawk
la riga di comando usa -f
per indicare quale file dovrebbe utilizzare come script, è necessario che sia sospeso -f
in modo che l'esecuzione di letter-freq.awk
alla shell verrà interpretato correttamente come in esecuzione /usr/bin/gawk -f letter-freq.awk
invece.
Posso testare lo script con pochi semplici input. Ad esempio, se inserisco l'alfabeto nel mio gawk
script, ogni lettera dovrebbe avere una frequenza relativa di 1:
$ echo abcdefghijklmnopqrstuvwxyz | gawk -f letter-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 1
Ripetendo quell'esempio ma aggiungendo un'istanza aggiuntiva della lettera e stamperà la lettera e con una frequenza relativa di 2 e ogni altra lettera come 1:
$ echo abcdeefghijklmnopqrstuvwxyz | gawk -f letter-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 1
E ora posso fare il grande passo! Userò il grep
comando con il /usr/share/dict/words
file e identifica la frequenza delle lettere per tutte le parole scritte interamente con lettere minuscole:
$ grep '^[a-z]*$' /usr/share/dict/words | gawk -f letter-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 2
Di tutte le parole minuscole nel /usr/share/dict/words
file, le lettere j , q e x si verificano meno frequentemente. La lettera z è anche piuttosto raro. Non a caso, la lettera e è il più utilizzato.