2015-09-14, por Alberto Simões ambs@cpan.org
Um exercício simples e típico, quando aprendemos uma nova linguagem de programação, é a escrita de um pequeno programa que, dado um ficheiro de texto, calcula quantas vezes cada palavra ocorre.
E para variar, vamos processar os parâmetros do programa à moda do
Perl 5, olhando para o @ARGV
que, em Perl 6, se chama @*ARGS
. A razão
daquele asterisco será detalhada noutra entrada.
use v6;
my $filename = shift @*ARGS;
O passo seguinte é verificar se o nome do ficheiro foi, realmente, indicado e, em caso afirmativo, verificar se este é, ou não, um ficheiro:
if !$filename || $filename.IO !~~ :f {
die "filename not specified or not existent."
}
Para além da falta de parentesis a que nos começamos a habituar, a segunda parte
da comparação apresenta algumas novidades. O ~~
é o smart match, um operador
que, embora em estado experimental, já foi introduzido no Perl 5. A sua negação é,
então, !~~
. Ou seja, estamos a verificar se $filename.IO
faz match a um :f
.
O método IO
sobre o nome do ficheiro transforma-o num handler de ficheiro (com
esse nome). O :f
é uma propriedade equialente ao -f
do Perl 5. Se tentarmos ler
este código poderíamos dizer algo como *se o nome do ficheiro não estiver definido
ou se o ficheiro não existir (e não for um ficheiro convencional), então o programa
deverá terminar.
my $contents = slurp $filename;
my %histogram;
for $contents ~~ m:global/\w+/ -> $word {
%histogram{$word.lc}++;
}
Segue-se a leitura do ficheiro, como um todo, para uma variável. Declara-se então a variável onde se armazenará o histograma.
O ciclo for
inclui dois pormenores distintos. Em primeiro lugar a sintaxe do
ciclo, que deixa de necessitar dos parentesis, e que troca a ordem da lista e da
variável iteradora. Antes da seta ->
devemos indicar a lista a ser percorrida,
e de seguida, a variável que irá tomar o valor de cada um dos elementos da lista.
Para calcular a lista de palavras usamos expressões regulares típicas. Tal como no
Perl 5, \w
corresponde a um caracter alfanumérico, e portanto estamos à procura de
todas as sequências de caracteres alfanuméricos. Por sua vez, o m:global
tem duas
partes: o m
é o operador de matching, tal como já existia em Perl 5; o :global
é um advérbio que indica que esse match deve ser global (tal como o /s
do Perl 5). Então, $contents ~~ m:global/\w+/
retorna uma lista de todas as palavras
encontradas.
Já o corpo do ciclo é o habitual, incrementando o número de ocorrências de cada
palavra (depois de a ter transformado em minúsculas, com o método lc
).
Só nos falta apresentar o resultado do histograma, ordenando as palavras por ocorrência, em ordem decrescente.
for %histogram.keys.sort({ %histogram{$^b} <=> %histogram{$^a}}) -> $key {
say "$key ==> %histogram{$key} occurrences";
}
Ora, para todas as chaves da variável %histogram
ordenámo-las. Como não queremos
ordenar as palavras por ordem alfabética, mas pelo número de ocorrências, temos
de, tal como em Perl 5, indicar uma função de ordenação. Em Perl 6 as variáveis
para a comparação são $^a
e $^b
, ao contrário das $a
e $b
do Perl 5. À
parte desse detalhe, a sintaxe deverá ser fácil de compreender.
Executando o script sobre o ficheiro onde este texto está escrito, obtemos um resultado semelhante ao seguinte:
ambs$ perl6 teste3.pl6 perl6_3.md
o ==> 25 occurrences
a ==> 23 occurrences
de ==> 21 occurrences
do ==> 12 occurrences
perl ==> 11 occurrences
um ==> 11 occurrences
e ==> 10 occurrences
se ==> 10 occurrences
que ==> 9 occurrences
ficheiro ==> 9 occurrences
é ==> 9 occurrences
[...]