[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