Como $_ é diferente de argumentos de entrada ou loop nomeados?
Pergunta
Como uso muito $_ quero entender melhor seu uso.$_ é uma variável global para valores implícitos, tanto quanto eu entendi e usei.
Como $_ parece estar definido de qualquer maneira, existem razões para usar variáveis de loop nomeadas em $_ além do mais legibilidade?
Em que casos importa que $_ seja uma variável global?
Então se eu usar
for (@array){
print $_;
}
ou mesmo
print $_ for @array;
tem o mesmo efeito que
for my $var (@array){
print $var;
}
Mas funciona da mesma forma?Eu acho que não exatamente, mas quais são as diferenças reais?
Atualizar:
Parece que $_ tem o escopo correto neste exemplo.Não é mais global?Estou usando 5.12.3.
#!/usr/bin/perl
use strict;
use warnings;
my @array = qw/one two three four/;
my @other_array = qw/1 2 3 4/;
for (@array){
for (@other_array){
print $_;
}
print $_;
}
que imprime corretamente 1234one1234two1234três1234four.
Para $_ global eu teria esperado 1234 4 1234 4 1234 4 1234 4 ..ou estou faltando algo óbvio?
Quando $_ é global então?
Atualizar:
Ok, depois de ler as várias respostas e o perlsyn com mais atenção, cheguei a uma conclusão:
Além da legibilidade, é melhor evitar o uso de $_ porque a localização implícita de $_ deve ser conhecida e levada em consideração, caso contrário, poderá encontrar um comportamento inesperado.
Obrigado pelo esclarecimento desse assunto.
Solução
are there reasons to use named loop variables over $_ besides readability?
A questão não é se eles são nomeados ou não.A questão é se elas são "variáveis de pacote" ou "variáveis lexicais".
Veja a muito boa descrição dos 2 sistemas de variáveis usados em Perl "Coping with Scoping":
http://perl.plover.com/FAQs/Namespaces.html
variáveis de pacote são variáveis globais e devem, portanto, ser evitadas por todas as razões usuais (por exemplo.ação à distância).
Evitar variáveis de pacote é uma questão de "operação correta" ou "mais difícil de injetar bugs" ao invés de uma questão de "legibilidade".
In what cases does it matter $_ is a global variable?
Em todos os lugares.
A melhor pergunta é:
In what cases is $_ local()ized for me?
Existem alguns lugares onde o Perl irá local()izar $_ para você, principalmente foreach, grep e map.Todos os outros lugares exigem que você local()ize você mesmo, portanto você estará injetando um bug em potencial quando inevitavelmente se esquecer de fazê-lo.:-)
Outras dicas
O clássico modo de falha de uso $_
(implícita ou explicitamente) como uma variável de loop é
for $_ (@myarray) {
/(\d+)/ or die;
foo($1);
}
sub foo {
open(F, "foo_$_[0]") or die;
while (<F>) {
...
}
}
onde, porque a variável de loop em for
/foreach
está vinculado ao item da lista real, significa que o while (<F>)
substitui @myarray
com linhas lidas dos arquivos.
$_ é o mesmo que nomear a variável como no seu segundo exemplo da maneira como ela normalmente é usada.$_ é apenas um nome de variável padrão de atalho para o item atual no loop atual para economizar digitação ao fazer um loop simples e rápido.Costumo usar variáveis nomeadas em vez do padrão.Deixa mais claro o que é e se acontecer de eu precisar fazer um loop aninhado não há conflitos.
Como $_ é uma variável global, você pode obter valores inesperados se tentar usar o valor que tinha em um bloco de código anterior.O novo bloco de código pode fazer parte de um loop ou outra operação que insere seus próprios valores em $_, substituindo o que você esperava que estivesse lá.
O risco de usar $_ é que ele é global (a menos que você o localize com local $_
) e, portanto, se alguma função que você chama no seu loop também usa $_, os dois usos podem interferir.
Por razões que não são claras para mim, isso só me incomoda ocasionalmente, mas normalmente localizo $_ se o uso dentro de pacotes.
Não há nada de especial nisso $_
além disso, é o parâmetro padrão para muitas funções.Se você definir explicitamente o escopo lexical do seu $_
com my
, perl usará a versão local do $_
em vez do global.Não há nada de estranho nisso, é como qualquer outra variável nomeada.
sub p { print "[$_]"; } # Prints the global $_
# Compare and contrast
for my $_ (b1..b5) { for my $_ (a1..a5) { p } } print "\n"; # ex1
for my $_ (b1..b5) { for (a1..a5) { p } } print "\n"; # ex2
for (b1..b5) { for my $_ (a1..a5) { p } } print "\n"; # ex3
for (b1..b5) { for (a1..a5) { p } } print "\n"; # ex4
Você deve ficar um pouco confuso com a saída até descobrir que o perl preservará o valor original da variável do loop na saída do loop (veja perlsyn).
Observação ex2 acima.Aqui, o segundo loop está usando o escopo lexical $_
declarado no primeiro loop.Sutil, mas esperado.Novamente, este valor é preservado na saída, então os dois loops não interferir.