Есть ли в Perl изящный способ конвертировать undef в 0 вручную?

У меня есть фрагмент в таком виде:

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;

Реальность еще более запутана, поскольку речь идет о более чем двух переменных.

Что я действительно хочу написать, так это:

my $total = $some_href->{$code}{'A'} + $some_href->{$code}{'B'};

и undef правильно оценивается как 0, но я получаю эти предупреждения почти при каждом запуске:

Use of uninitialized value in addition (+) at Stats.pm line 192.

Как лучше убрать эти сообщения?

NB: я использую «строгие» и «использую предупреждения», если это уместно.


person Anon Gordon    schedule 29.08.2009    source источник
comment
Это актуально. В этом случае вы включили предупреждение, которое вас не волнует.   -  person jrockway    schedule 29.08.2009


Ответы (4)


Хорошо, что вы используете strict и warnings. Цель предупреждений — предупредить вас, когда Perl обнаружит поведение, которое, вероятно, будет непреднамеренным (и, следовательно, неверным). Когда вы делаете это преднамеренно, вполне нормально отключить предупреждение локально. undef рассматривается как 0 в числовом контексте. Если вас устраивает как наличие неопределенных значений, так и их нулевая оценка, просто отключите предупреждение:

my $total;
{
  no warnings 'uninitialized';
  $total = $some_href->{$code}{A} + $some_href->{$code}{B};
}

Примечание. Отключайте только те предупреждения, которые вам нужны, и делайте это в минимально возможной области.

Если вы не хотите отключать предупреждения, есть и другие варианты. Начиная с Perl 5.10 вы можете использовать оператор // (определенный-или) для установки значений по умолчанию. До этого люди часто использовали || (логическое ИЛИ), но это может привести к неправильному результату для значений, которые оцениваются как ложные. Надежный способ получить значения по умолчанию в версиях Perl до 5.10 — это проверить, являются ли они defined.

$x = $y // 42;             # 5.10+
$x = $y || 42;             # < 5.10 (fragile)
$x = defined $y ? $y : 42; # < 5.10 (robust)
person Michael Carman    schedule 29.08.2009
comment
Да, $y || 42 хрупкий, но $y || 0 не такой хрупкий. - person innaM; 29.08.2009

Вы можете на секунду отключить предупреждение о «неинициализации»:

my $a;
my $b = 1;
{
    no warnings 'uninitialized';
    my $c = $a+$b; # no warning
}
my $c = $a+$b; # warning

Или можно замкнуть на ноль:

my $d = ($a||0)+$b; # no warning

Хотя мне кажется не очень красиво.

person zoul    schedule 29.08.2009

Когда вы их добавляете, просто отфильтруйте undef.

use List::Util 'sum';

my $total = sum (0, grep {defined} $some_href->{$code}{'A'}, $some_href->{$code}{'B'});

Или даже

use List::Util 'sum';

my $total = sum (0, grep {defined} map {$some_href->{$code}{$_}} 'A', 'B');
person dave4420    schedule 29.08.2009

my $a = $some_href->{$code}{'A'} || 0;
my $b = $some_href->{$code}{'B'} || 0;
my $total = $a + $b;

В этом случае допустимо рассматривать ложные значения так же, как и неопределенные значения, из-за вашего резервного значения.

person glenn jackman    schedule 29.08.2009
comment
Этот код не совсем то же самое. Он также превращает пустую строку, определенное значение, в 0. Возможно, это не то, что вам нужно. - person brian d foy; 30.08.2009
comment
Пока он их добавлял, я предположил, что это именно то, что он хотел. - person glenn jackman; 30.08.2009