【问题标题】:In Perl, is there graceful way to convert undef to 0 manually?在 Perl 中,是否有手动将 undef 转换为 0 的优雅方法?
【发布时间】:2026-01-11 13:05:02
【问题描述】:

我有一个这种形式的片段:

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.

消除这些消息的最佳方法是什么?

注意:如果相关,我会“使用严格”和“使用警告”。

【问题讨论】:

  • 相关。在这种情况下,您启用了一个您不关心的警告。

标签: perl undef


【解决方案1】:

您使用strictwarnings 很好。警告的目的是在 Perl 发现可能是无意的(因此不正确的)行为时提醒您。当您故意这样做时,在本地禁用警告是完全可以的。 undef 在数字上下文中被视为 0。如果您可以同时拥有未定义的值并让它们评估为零,只需禁用警告:

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

注意:仅禁用您需要的警告,并在尽可能小的范围内这样做。

如果您不喜欢禁用警告,还有其他选择。从 Perl 5.10 开始,您可以使用 //(定义或)运算符来设置默认值。在此之前,人们经常使用||(逻辑或),但这对于评估为假的值可能会做错事。在 5.10 之前的 Perl 版本中获取默认值的可靠方法是检查它们是否为 defined

$x = $y // 42;             # 5.10+
$x = $y || 42;             # < 5.10 (fragile)
$x = defined $y ? $y : 42; # < 5.10 (robust)

【讨论】:

  • 是的,“$y || 42”很脆弱,但“$y || 0”没有那么脆弱。
【解决方案2】:
my $a = $some_href->{$code}{'A'} || 0;
my $b = $some_href->{$code}{'B'} || 0;
my $total = $a + $b;

在这种情况下,由于您的后备值,可以将错误值视为未定义值。

【讨论】:

  • 这段代码做的不太一样。它还将空字符串(一个定义的值)转换为 0。这可能不是您想要的。
  • 我认为,当他添加它们时,这实际上是他想要的。
【解决方案3】:

在添加它们时,只需过滤掉 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');

【讨论】:

    【解决方案4】:

    您可以关闭“未初始化”警告一秒钟:

    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
    

    不过对我来说不是很好。

    【讨论】: