以下两个Perl变量声明之间有什么区别?
my $foo = 'bar' if 0;
my $baz;
$baz = 'qux' if 0;
当这些出现在循环的顶部时,差异是显著的。例如:
use warnings;
use strict;
foreach my $n (0,1){
my $foo = 'bar' if 0;
print defined $foo ? "definedn" : "undefinedn";
$foo = 'bar';
print defined $foo ? "definedn" : "undefinedn";
}
print "==n";
foreach my $m (0,1){
my $baz;
$baz = 'qux' if 0;
print defined $baz ? "definedn" : "undefinedn";
$baz = 'qux';
print defined $baz ? "definedn" : "undefinedn";
}
中的结果
undefined
defined
defined
defined
==
undefined
defined
undefined
defined
似乎if 0
失败了,所以foo
永远不会重新初始化为undef
。在这种情况下,它首先是如何声明的?
首先,请注意,my $foo = 'bar' if 0;
被记录为未定义的行为,这意味着它可以做任何事情,包括崩溃。但我会解释到底会发生什么。
my $x
有三个记录在案的效应:
- 它在编译时声明一个符号
- 它在执行时创建一个新变量
- 它在执行时返回新变量
简而言之,假设它与Java的Scalar x = new Scalar();
类似,只是如果在表达式中使用,它会返回变量。
但如果它真的是这样工作的话,下面将创建100个变量:
for (1..100) {
my $x = rand();
print "$xn";
}
这意味着对于单独的my
,每个循环迭代将分配两到三个内存!一个非常昂贵的前景。相反,Perl只创建一个变量,并在作用域结束时将其清除。所以在现实中,my $x
实际上做了以下事情:
- 它在编译时声明一个符号
- 它在编译时创建变量[1]
- 它在堆栈上放置一个指令,当作用域退出时,该指令将清除变量[2]
- 它在执行时返回新变量
因此,只有一个变量被创建[2]。这比每次输入作用域时创建一个作用域的CPU效率高得多。
现在考虑一下,如果有条件地执行my
,或者根本不执行,会发生什么。这样做可以防止它在堆栈上放置清除变量的指令,这样变量就永远不会丢失其值。显然,这是不应该发生的,所以my ... if ...;
是不被允许的。
一些人利用了以下实现:
sub foo {
my $state if 0;
$state = 5 if !defined($state);
print "$staten";
++$state;
}
foo(); # 5
foo(); # 6
foo(); # 7
但这样做需要忽略禁止它的文件。以上可以使用安全地实现
{
my $state = 5;
sub foo {
print "$staten";
++$state;
}
}
或
use feature qw( state ); # Or: use 5.010;
sub foo {
state $state = 5;
print "$staten";
++$state;
}
注:
"变量"可以指几件事。我不确定这里哪个定义是准确的,但没关系。
如果除了子本身之外的任何内容都包含对变量的引用(REFCNT>1),或者如果变量包含对象,则指令将用新变量替换该变量(在作用域出口上),而不是清除现有变量。这允许以下功能正常工作:
my @a; for (...) { my $x = ...; push @a, $x; }
请参阅ikegami的更好答案,可能在上面。
在第一个例子中,由于条件的原因,您从未在循环中定义$foo,因此当您使用它时,您将引用一个隐式声明的全局变量,然后将其赋值。然后,第二次通过循环,外部变量已经定义。
在第二个示例中,每次执行块时都在块内部定义$baz。因此,第二次循环时,它是一个新的、尚未定义的局部变量。