如何在Perl中检查变量是否已声明?

9

我在使用Perl中的use strict;,并且我使用以下语句。

unless(defined($x)){
      print "Not defined";
}

变量$x没有被声明。所以我期望它打印出 "Not defined" 但是它返回了一个错误。

Global symbol "$x" requires explicit package name at *********** in line 15.

8
为什么想要这样做?代码应该已经知道有哪些变量和命名空间可以使用,不应该需要进行检查。 - Ether
@Ether 有很多原因。它类似于 C 中的 #define <>。你想要过滤掉代码中的调试打印语句。 - Sujay Phadke
9个回答

19

strict 严格语法检查模块有三个部分:严格引用、严格变量和严格子程序。你遇到的是:

strict vars 严格变量

如果访问了一个没有通过 ouruse vars 声明,或者没有被通过 my 局部化,或者没有被完全限定的变量,这将生成一个编译时错误。因为这是为了防止变量自杀问题和微妙的动态作用域问题,所以仅仅使用 local 变量是不够的。

由于它会生成编译时错误,因此您的非 BEGIN 代码甚至没有机会运行。您可以在代码块内暂时允许非严格变量,如下所示:

{
  no strict 'vars';
  print "Not defined!\n" unless defined $x;
}

但请注意,Perl的defined运算符告诉您一个值是否被定义,而不是一个变量是否已经声明。

告诉我们更多关于你的应用程序,我们可以给你更好的建议来处理它。


2
重要的部分是区分未声明和未定义。 - Svante

6
你甚至不能引用未声明的变量。当你询问一个未定义的变量时,会产生错误。
defined( $x ) ?

编译器会抱怨:我不知道你在问什么,我怎么告诉它这是已定义的?由于你指示不想按名称自动创建变量,因此它没有该变量的参考点。
如果未开启strict 'vars'(默认情况下使用use strict),则会为“x”在包符号表中创建一个条目。
有趣的是,即使没有开启strict 'refs',也很容易检查变量是否在包符号表中。
defined( *{ __PACKAGE__ . '::x' }{SCALAR} )

由于没有自动创建词法变量(“my变量”)的方式,也没有标准的方法来检查词法变量是否已声明。词法变量存储在“pad”中。但有一个名为PadWalker的模块可以帮助解决这个问题。
为了检查当前级别,您可以获取pad的哈希值,然后检查它是否存在于当前pad中。您还可以通过堆栈向上循环回溯(整数参数类似于caller),以找到最近的x所在位置。
my $h = peek_my (0);
exists $h->{x};

6
我认为你混淆了“定义”和“声明”的概念。
你询问的是“如何检查变量是否在perl中声明”,但然后你又检查变量是否被定义。这是两个不同的概念。
在perl中,如果使用“use strict”,则会自动检查任何未声明的变量(使用“my”,“local”或“our”)。一旦有一个变量声明,就可以测试它是否被定义(分配了一个值)。
因此,在你的测试中,在测试是否已定义之前,你缺少先前的声明。
use strict;
my $x;  # you are missing this part
[...] | # code
# your test for define
print defined $x? "defined\n" : "not defined\n";

请注意,仅对$x进行测试并不适合您的目的:
my ($x,$y, $z);
$w;         # not declared (use strict will catch it and die)
$x = 0;     # declared and defined BUT if you make a logic test like 'if ($x) {}' then it will be FALSE, so don't confuse testing for **'$x'** and testing for **'defined $x'**
$y = undef; # declared but not defined
$z = 1;     # declared, defined, and logial test TRUE

最后,我认为xenorraticide的答案有误:他建议使用'unless $x'来测试是否已定义,这是不正确的。他还建议使用'unless exists $x',这对于测试标量是错误的。'exists'测试仅适用于哈希键(对于数组而言已被弃用)。
希望这可以帮到您。

1

通常这种代码不应该在一个严肃的程序中使用,但为什么不只是为了好玩呢:(假设使用严格模式)

print "Not defined\n" unless eval 'ref(\$x)';

1
 #
 print "Not defined" if !defined($x);

结果将会是

未定义

 #
 use strict;
 print "Not defined" if !defined($x);

将会生成与您的问题类似的错误。

请查看:http://perldoc.perl.org/strict.html,其中描述了如何仅导入所需的限制。(但使用 strict 'vars' 是一个非常好的想法 :))


1

似乎可以通过以下方式检查变量是否已声明(如 man perlmod 中的“符号表”部分所述):

main::(-e:1):   1
  DB<1> x __PACKAGE__
0  'main'
  DB<2> x exists ${__PACKAGE__ . '::'}{xyz}
0  ''
  DB<3> $xyz = undef
  DB<4> x exists ${__PACKAGE__ . '::'}{xyz}
0  1
  DB<5> $xyz = 7
  DB<6> x exists ${__PACKAGE__ . '::'}{xyz}
0  1

所以,在执行 perl -d -e 1 后,你会进入包 main,其中 $xyz 没有被声明。 因此 ("全局") xyzmain 的符号表中不存在。 但是,将 undef 赋值给该名称的变量后,符号表中就会有一个条目。
然而,这种机制对于 my 变量(词法作用域)不起作用。

0
#!/usr/bin/perl -l

use strict;

# if string below commented out, prints 'lol' , if the string enabled, prints 'eeeeeeeee'
#my $lol = 'eeeeeeeeeee' ;
# no errors or warnings at any case, despite of 'strict'

our $lol = eval {$lol} || 'lol' ;

print $lol;

0
我的解决方案是 #!/usr/bin/perl -l
use strict;

# if string below commented out, prints 'lol' , if the string enabled, prints 'eeeeeeeee'
#my $lol = 'eeeeeeeeeee' ;
# no errors or warnings at any case, despite of 'strict'

our $lol = eval {$lol} || 'lol' ;

print $lol;

-1

您可以像这样使用unless

use 'strict';

my $var = 'defined';
unless (defined($var)) {
  print "not defined\n";
}

$var = undef;
unless (defined($var)) {
  print "not defined\n";
}

第一个print语句不会输出任何内容,而第二个则会,因为$var已经被设置为undef


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接