Em Perl, existe uma maneira elegante de converter undef em 0 manualmente?
Pergunta
Eu tenho um fragmento neste formato:
my $a = $some_href->{$code}{'A'}; # a number or undef
my $b = $some_href->{$code}{'B'}; # a number or undef
$a = 0 unless defined($a);
$b = 0 unless defined($b);
my $total = $a + $b;
A realidade é ainda mais confusa, uma vez que estão em causa mais de duas variáveis.
O que eu realmente quero escrever é isto:
my $total = $some_href->{$code}{'A'} + $some_href->{$code}{'B'};
e undef foi avaliado corretamente como 0, mas recebo esses avisos em quase todas as execuções:
Use of uninitialized value in addition (+) at Stats.pm line 192.
Qual é a melhor maneira de fazer essas mensagens desaparecerem?
Observação:Eu 'uso estrito' e 'uso avisos' se isso for relevante.
Solução
Que bom que você está usando strict
e warnings
.O objetivo dos avisos é alertá-lo quando Perl vê um comportamento que provavelmente não é intencional (e, portanto, incorreto).Quando você faz isso deliberadamente, é perfeitamente normal desativar o aviso localmente. undef
é tratado como 0
em contextos numéricos.Se você concorda em ter valores indefinidos e avaliá-los como zero, apenas desative o aviso:
my $total;
{
no warnings 'uninitialized';
$total = $some_href->{$code}{A} + $some_href->{$code}{B};
}
Observação:Desative apenas os avisos necessários e faça-o no menor escopo possível.
Se você não gosta de desativar avisos, existem outras opções.A partir do Perl 5.10 você pode usar o //
Operador (definido ou) para definir valores padrão.Antes disso, as pessoas costumavam usar o ||
(lógico-ou), mas isso pode fazer a coisa errada para valores avaliados como falsos.A maneira robusta de definir valores padrão em versões anteriores à 5.10 do Perl é verificar se eles estão defined
.
$x = $y // 42; # 5.10+
$x = $y || 42; # < 5.10 (fragile)
$x = defined $y ? $y : 42; # < 5.10 (robust)
Outras dicas
Você pode desligar o aviso “não inicializado” por um segundo:
my $a;
my $b = 1;
{
no warnings 'uninitialized';
my $c = $a+$b; # no warning
}
my $c = $a+$b; # warning
Ou você pode entrar em curto-circuito para zero:
my $d = ($a||0)+$b; # no warning
Mas não parece muito legal para mim.
Ao adicioná-los, basta filtrar os undefs.
use List::Util 'sum';
my $total = sum (0, grep {defined} $some_href->{$code}{'A'}, $some_href->{$code}{'B'});
Ou mesmo
use List::Util 'sum';
my $total = sum (0, grep {defined} map {$some_href->{$code}{$_}} 'A', 'B');
my $a = $some_href->{$code}{'A'} || 0;
my $b = $some_href->{$code}{'B'} || 0;
my $total = $a + $b;
Nesse caso, não há problema em tratar valores falsos da mesma forma que valores indefinidos por causa do seu valor substituto.