16

启用警告后,perl 通常会打印Use of uninitialized value $fooif$foo在表达式中使用并且没有被赋值,但在某些情况下它是可以的,并且变量被视为false0''没有警告。

在哪些情况下可以在没有警告的情况下使用未初始化/未定义的变量?

4

4 回答 4

19

概括

  • 布尔测试
  • 增加或减少未定义的值
  • 附加到未定义的值
  • 自生化
  • 其他突变体

布尔测试

根据perlsyn 文档

在布尔上下文中,数字0、字符串'0'''、空列表()undef都是假的。所有其他值都为真。

因为未定义的值为false,所以下面的程序

#! /usr/bin/perl

use warnings;

my $var;
print "A\n" if $var;
$var && print "B\n";
$var and print "C\n";
print "D\n" if !$var;
print "E\n" if not $var;
$var or print "F\n";
$var || print "G\n";

D通过输出G没有警告。

增加或减少未定义的值

如果您的代码将至少增加或减少一次标量,则无需将标量显式初始化为零:

#! /usr/bin/perl

use warnings;

my $i;
++$i while "aaba" =~ /a/g;
print $i, "\n";

上面的代码输出3没有警告。

附加到未定义的值

与隐式零类似,如果您至少附加一次,则无需将标量显式初始化为空字符串:

#! /usr/bin/perl

use warnings;
use strict;

my $str;
for (<*>) {
  $str .= substr $_, 0, 1;
}
print $str, "\n";

自生化

一个例子是“自动复活”。来自维基百科的文章

Autovivification 是 Perl 编程语言的一个显着特征,它涉及数据结构的动态创建。自动激活是在取消引用未定义的值时自动创建变量引用。换句话说,Perl 自动激活允许程序员引用结构化变量和该结构化变量的任意子元素,而无需事先明确声明变量的存在及其完整结构。

例如:

#! /usr/bin/perl

use warnings;

my %foo;
++$foo{bar}{baz}{quux};

use Data::Dumper;
$Data::Dumper::Indent = 1;
print Dumper \%foo;

即使我们没有显式初始化中间键,Perl 也会负责脚手架:

$VAR1 = {
  '酒吧' => {
    '巴兹' => {
      'quux' => '1'
    }
  }
};

如果没有自动生存,代码将需要更多样板:

my %foo;
$foo{bar} = {};
$foo{bar}{baz} = {};
++$foo{bar}{baz}{quux};  # finally!

不要将自动生存与它可能产生的未定义值混淆。例如与

#! /usr/bin/perl

use warnings;

my %foo;
print $foo{bar}{baz}{quux}, "\n";
use Data::Dumper;
$Data::Dumper::Indent = 1;
print Dumper \%foo;

我们得到

在 ./prog.pl 第 6 行的打印中使用未初始化的值。

$VAR1 = {
  '酒吧' => {
    '巴兹' => {}
  }
};

请注意,中间键自动激活。

自动复活的其他例子:

  • 引用数组

    my $a;
    push @$a => "foo";
    
  • 对标量的引用

    my $s;
    ++$$s;
    
  • 参考哈希

    my $h;
    $h->{foo} = "bar";
    

遗憾的是,Perl (还没有!)自动激活以下内容:

my $code;
$code->("Do what I need please!");

其他突变体

在回答类似问题时,ysth 报道

某些运算符为了您的方便而故意忽略“未初始化”警告,因为它们通常用于左侧或唯一操作数的默认值 0 或“”有意义的情况。

它们是:++和(前--或后)+=,,,,,,,,,,。-=.=|=^=&&=||=

作为“定义或”,//=很高兴地在没有警告的情况下改变一个未定义的值。

于 2010-01-30T02:32:51.893 回答
2

到目前为止,我发现的案例是:

  • 自动存活(gbacon的回答
  • 布尔上下文,例如if $foo$foo || $bar
  • ++--
  • +=, -=, 或的左侧.=

还有其他人吗?

于 2010-01-30T02:54:10.403 回答
2

始终修复警告,即使是讨厌的恼人的警告。

可以关闭未定义的警告。您可以通过为操作创建新范围来做到这一点。有关perldoc perllexwarn更多信息,请参阅。此方法适用于所有版本的 perl。

{
  no warnings 'uninitialized';
  my $foo = "foo" + undef = "bar";
}

对于很多二元运算符,您可以使用新的 Perl 5.10 东西,~~并且//; 有关perldoc perlop更多信息,请参阅。

use warnings;
my $foo = undef;
my $bar = $foo // ''; ## same as $bar = defined $foo ? $foo : ''

//=如果变量未定义,也是设置变量的变体:

$foo //= '';

Smart Matching ( ~~) 运算符很酷,并且允许进行智能比较,这很不错,请查看perldoc perlsyn

use warnings;
my $foo = "string";
say $foo eq undef;  # triggers warnings
say $foo ~~ undef;  # no undef warnings
于 2010-01-31T17:14:34.697 回答
0

真正的答案应该是:为什么要打开该警告?undef 是一个非常好的变量值(任何使用过数据库的人都可以告诉你),区分真(发生了某事)、假(什么都没发生)和 undef(发生了错误)通常是有意义的。

而不是说

use strict;
use warnings;

use common::sense;

并且您将获得警告的所有好处,但会关闭未定义变量等烦人的警告。

common::sense 可从 CPAN 获得。

于 2010-01-31T16:43:43.537 回答