Nedávno jsem začal psát hru, kde skládáte slova pomocí dlaždic s písmeny. K vytvoření hry jsem potřeboval znát četnost písmen v běžných slovech v anglickém jazyce, abych mohl prezentovat užitečnou sadu dlaždic s písmeny. Frekvence písmen je diskutována na různých místech, včetně Wikipedie, ale chtěl jsem si frekvenci písmen vypočítat sám.
Linux poskytuje seznam slov v /usr/share/dict/words
soubor, takže už mám seznam pravděpodobných slov k použití. words
soubor obsahuje spoustu slov, která chci, ale několik ne. Chtěl jsem seznam všech slov, která nebyla složená slova (žádné pomlčky nebo mezery) nebo vlastní podstatná jména (žádná velká písmena). K získání tohoto seznamu mohu spustit grep
příkaz k vytažení pouze řádků, které se skládají pouze z malých písmen:
$ grep '^[a-z]*$' /usr/share/dict/words
Tento regulární výraz se ptá grep
aby odpovídalo vzorům, které jsou pouze malými písmeny. Znaky ^
a $
ve vzoru představují začátek a konec řádku. [a-z]
seskupení bude odpovídat pouze malým písmenům a do z .
Zde je rychlá ukázka výstupu:
$ grep '^[a-z]*$' /usr/share/dict/words | head
a
aa
aaa
aah
aahed
aahing
aahs
aal
aalii
aaliis
Další zdroje pro Linux
- Cheat pro příkazy Linuxu
- Cheat sheet pro pokročilé příkazy systému Linux
- Bezplatný online kurz:Technický přehled RHEL
- Síťový cheat pro Linux
- Cheat sheet SELinux
- Cheat pro běžné příkazy pro Linux
- Co jsou kontejnery systému Linux?
- Naše nejnovější články o Linuxu
A ano, to všechno jsou platná slova. Například „aahed“ je zvolání minulého času „aah“, jako při relaxaci. A "aalii" je hustý tropický keř.
Teď už jen potřebuji napsat gawk
skript, který provede práci počítání písmen v každém slově a poté vytiskne relativní frekvenci každého nalezeného písmene.
Počítání písmen
Jeden způsob, jak počítat písmena v gawk
je iterovat každý znak v každém vstupním řádku a počítat výskyty každého písmene a do z . substr
funkce vrátí podřetězec dané délky, například jedno písmeno, z většího řetězce. Tento příklad kódu například vyhodnotí každý znak c
ze vstupu:
{
len = length($0); for (i = 1; i <= len; i++) {
c = substr($0, i, 1);
}
}
Pokud začnu globálním řetězcem LETTERS
který obsahuje abecedu, mohu použít index
funkce k nalezení umístění jednoho písmene v abecedě. Rozbalím gawk
příklad kódu pro vyhodnocení pouze písmen a do z ve vstupu:
BEGIN { LETTERS = "abcdefghijklmnopqrstuvwxyz" }
{
len = length($0); for (i = 1; i <= len; i++) {
c = substr($0, i, 1);
ltr = index(LETTERS, c);
}
}
Všimněte si, že funkce index vrací první výskyt písmene z LETTERS
řetězec začínající 1 na prvním písmenu nebo nulou, pokud nebyl nalezen. Pokud mám pole dlouhé 26 prvků, mohu pole použít k počítání výskytů každého písmene. Toto přidám do svého příkladu kódu pro zvýšení (pomocí ++
) počet pro každé písmeno, jak je uvedeno ve vstupu:
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];
}
}
}
Relativní frekvence tisku
Po gawk
skript počítá všechna písmena, chci vytisknout frekvenci každého písmena, které najde. Nezajímá mě celkový počet jednotlivých písmen ze vstupu, ale spíše relativní četnost každého písmene. Relativní četnost měří počty tak, aby písmeno s nejmenším počtem výskytů (například písmeno q ) je nastaveno na 1 a ostatní písmena se vztahují k této hodnotě.
Začnu počtem písmen a a poté tuto hodnotu porovnejte s počty pro každé z ostatních písmen b do z :
END {
min = count[1]; for (ltr = 2; ltr <= 26; ltr++) {
if (count[ltr] < min) {
min = count[ltr];
}
}
}
Na konci této smyčky je proměnná min
obsahuje minimální počet pro každé písmeno. Mohu to použít k poskytnutí měřítka pro počty k vytištění relativní frekvence každého písmene. Pokud je například písmeno s nejnižším výskytem q a poté min
se bude rovnat q počítat.
Poté procházím každé písmeno a vytisknu ho s jeho relativní frekvencí. Každý počet vydělím min
vytisknout relativní četnost, což znamená, že písmeno s nejnižším počtem bude vytištěno s relativní četností 1. Pokud se jiné písmeno objeví dvakrát častěji než nejnižší počet, bude mít toto písmeno relativní četnost 2. zajímají mě zde celočíselné hodnoty, takže 2.1 a 2.9 jsou pro mé účely stejné jako 2:
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);
}
}
Dáme vše dohromady
Nyní mám gawk
skript, který dokáže počítat relativní četnost písmen ve svém vstupu:
#!/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);
}
}
Uložím to do souboru s názvem letter-freq.awk
abych jej mohl snadněji používat z příkazového řádku.
Pokud chcete, můžete také použít chmod +x
aby byl soubor spustitelný samostatně. #!/usr/bin/gawk -f
na prvním řádku znamená, že Linux jej spustí jako skript pomocí /usr/bin/gawk
program. A protože gawk
příkazový řádek používá -f
k určení, který soubor má použít jako skript, potřebujete, aby visel -f
takže spuštění letter-freq.awk
v shellu bude správně interpretováno jako spuštění /usr/bin/gawk -f letter-freq.awk
místo toho.
Skript mohu otestovat pomocí několika jednoduchých vstupů. Pokud například do svého gawk
vložím abecedu script, každé písmeno by mělo mít relativní četnost 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
Opakujeme tento příklad, ale přidáme další výskyt písmene e vytiskne písmeno e s relativní četností 2 a každé další písmeno jako 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
A teď můžu udělat velký krok! Použiji grep
pomocí příkazu /usr/share/dict/words
soubor a určete frekvenci písmen pro všechna slova napsaná výhradně malými písmeny:
$ 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
Ze všech malých písmen v /usr/share/dict/words
soubor, písmena j , q a x vyskytují nejméně často. Písmeno z je také poměrně vzácný. Není překvapením, že písmeno e je nejčastěji používaný.