下面两个 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 ? "defined\n" : "undefined\n";
$foo = 'bar';
print defined $foo ? "defined\n" : "undefined\n";
}
print "==\n";
foreach my $m (0,1){
my $baz;
$baz = 'qux' if 0;
print defined $baz ? "defined\n" : "undefined\n";
$baz = 'qux';
print defined $baz ? "defined\n" : "undefined\n";
}
结果
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 "$x\n";
}
这意味着单独为 my
的每个循环迭代分配两到三个内存!一个非常昂贵的前景。相反,Perl 只创建一个变量并在作用域结束时将其清除。所以实际上,my $x
实际上做了以下事情:
- 它在编译时声明一个符号。
- 它在编译时创建变量[1]。
- 它在堆栈上放置一个指令,该指令将在退出作用域时清除[2]变量。
- 它在执行时返回新变量。
因此,只会创建一个变量[2]。这比每次输入范围时都创建一个 CPU 效率更高。
现在考虑如果您有条件地执行 my
或根本不执行会发生什么。通过这样做,您可以防止它放置指令来清除堆栈上的变量,因此变量永远不会丢失其值。显然,这不应该发生,所以这就是为什么 my ... if ...;
是不允许的。
一些利用实现如下:
sub foo {
my $state if 0;
$state = 5 if !defined($state);
print "$state\n";
++$state;
}
foo(); # 5
foo(); # 6
foo(); # 7
但是这样做需要忽略禁止它的文档。以上可以安全地使用
{
my $state = 5;
sub foo {
print "$state\n";
++$state;
}
}
或
use feature qw( state ); # Or: use 5.010;
sub foo {
state $state = 5;
print "$state\n";
++$state;
}
注意事项:
“变量”可以有多种含义。我不确定这里哪个定义是准确的,但这并不重要。
如果除了 sub 本身之外还有对变量的引用 (REFCNT>1),或者如果变量包含一个对象,指令将用一个新变量替换该变量(在作用域退出时)而不是清除现有变量.这允许以下内容正常工作:
my @a; for (...) { my $x = ...; push @a, \$x; }
关于perl - 为什么这个 Perl 变量保持它的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17356168/