[Cascavel-pm] Res: Perl

Adriano Ferreira a.r.ferreira em gmail.com
Segunda Novembro 27 08:08:46 PST 2006


On 11/27/06, Nelson Ferraz <nferraz em gmail.com> wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Eden Cardim wrote:
> > "One of the most efficient ways for Perl programmers to bring misery
> > and suffering upon themselves and their colleagues is to write this:
> >     open FILE, '<', $filename
> >         or croak "Can't open '$filename': $OS_ERROR"; "
>
> Sei lá... eu ainda acho a forma "canônica" perfeitamente válida:
>
>     open (FILE, $filename) or die "Can't read '$filename': $!";
>
> Existem outras "best practices" muito mais úteis do que essa, IMO.

De fato! O problema é que globs são globais (bem, isto é uma meia
verdade, são globais no package em que são declarados). Se o package
onde o código é escrito é (ab)usado e cada programador adicional
acrescenta sua programação a ele, vai ser mais fácil esbarrar com
casos de conflitos. É o caso do uso de pedaços como a seguir.

Digamos que você tem uma função que abre um arquivo através do glob F
e enquanto o lê, chama outras funções. Como esta:

=item proc

    @a = map_f($fn, $sub);

Aplica a subrotina $sub a cada uma das linhas do
arquivo $fn (depois do C<chomp>) e reúne os resultados
em uma lista.

=cut

sub map_f {
    my $fn = shift;
    my $sub = shift;
    open F, '<', $fn or die $!;
    my @a;
    chomp, push @a, $sub->($_) while <F>;
    close F;
    return @a;
}

Agora você resolve usar este 'map' sobre as linhas de um arquivo de
tal forma que cada linha por si é um nome de arquivo e o resultado que
você quer é o conteúdo do arquivos listados. Então você escreve:

sub slurp_file {
    my $fn = shift;
    open F, '<', $fn or die $!;
    local $/;
    my $contents = <F>;
    close F;
    return $contents;
}

E tenta usar:

    @everything = map_f("lista_de_arquivos.txt", \&slurp_file);

que não vai funcionar porque no começo no começo F é aberto sobre
"lista_de_arquivos.txt" e logo depois sobre o arquivo cujo nome está
na primeira linha. O glob F é fechado por slurp_file e map_f não vai
saber voltar onde estava nem o que fazer com o arquivo fechado.

    $ perl my_script.pl                # map_f e slurp_file lá dentro
no mesmo package
    readline() on closed filehandle F at g.pl line 20.
    --> aqui o script morre, ou jaz

Tudo isto acontece se as subs estão no mesmo package. Se os globs
usados fossem diferentes, FILE em um e F no outro, este problema não
aconteceria. Se as subs estivessem em diferentes packages não
aconteceria. Se antevendo a possibilidade de usar slurp_file de dentro
de um código que podia mexer com o glob F, você usasse "local", este
problema não aconteceria.

sub slurp_file {
    my $fn = shift;
    local *F; # F é localizado aqui apenas para esta subrotina
    local $/;
    open F, '<', $fn or die $!;
    my $contents = <F>;
    close F;
    return $contents;
}

Como o Eden disse, podem haver mais problemas através da confusão de
barewords com funções e constantes de mesmo nome. Coisa que não
aconteceria se fossem usados:

    open my $f, '<', $fn or die $!;

(que precisa de perl >= 5.8)

A questão é que a maioria do código não vai bater com estas limitações
facilmente e muita gente continua programando feliz com o Perl seu de
cada dia, ignorante destas questões da programação de sistema
GRAAANDES.

Já dizia a Audrey, "Quando menos código, melhor." Menos trabalho para
programar, manter e explicar. Menos oportunidade para coisas darem
erradas. Pouco código, cada um em seu package é uma boa prática
também.

Desculpem pela digressão. Eu me empolguei.

Adriano.


Mais detalhes sobre a lista de discussão Cascavel-pm