Perl 变量作用域
Perl variables scoping
我想用 this 方法从数组中删除重复值。重复删除必须在循环内执行。
这是一个演示我遇到的问题的最小示例:
use strict;
for (0..1){
my %seen;
sub testUnique{
return !$seen{shift}++;
}
my $res = testUnique(1);
if($res){
print "unique\n";
}else{
print "non-unique\n";
}
}
我在循环中定义了 %seen
散列,因此我希望它仅在循环的单次迭代期间定义。
然而,上面代码的结果是:
unique
non-unique
通过一些调试打印,我发现 %seen
的值从一个迭代到另一个迭代被保留。
我尝试了一个简单的
for (0..1){
my %seen;
$seen{1}++;
print "$seen{1}\n";
}
这一个按预期工作。它打印:
1
1
所以,我猜问题出在内部函数 testUnique
上。
有人能给我解释一下这是怎么回事吗?
您的 testUnique
子关闭超过 %seen
的第一个实例。即使它在 for
循环内,子例程也不会重复编译。
您的代码被编译一次,包括 在 for
循环 的顶部初始化词法范围变量 %hash
的部分。 =16=]
以下将产生您想要的输出,但我不确定我看到的是沿着这条路走下去:
#!/usr/bin/env perl
use warnings;
use strict;
for (0..1){
my %seen;
my $tester = sub {
return !$seen{shift}++;
};
print $tester->(1) ? "unique\n" : "not unique\n";
}
子例程只能定义一次,不会为循环的每次迭代重新创建。因此,它只包含对初始 %seen
哈希的引用。添加一些输出有助于澄清这一点:
use strict;
use warnings;
for(0 .. 1) {
my %seen = ();
print "Just created " . \%seen . "\n";
sub testUnique {
print "Testing " . \%seen . "\n";
return ! $seen{shift} ++;
}
if(testUnique(1)) {
print "unique\n";
}
else {
print "non-unique\n";
}
}
输出:
Just created HASH(0x994fc18)
Testing HASH(0x994fc18)
unique
Just created HASH(0x993f048)
Testing HASH(0x994fc18)
non-unique
这里可以看出,初始哈希是唯一被测试的。
欢迎来到闭包的世界。
sub make_closure {
my $counter = 0;
return sub { return ++$counter };
}
my $counter1 = make_closure();
my $counter2 = make_closure();
say $counter1->(); # 1
say $counter1->(); # 2
say $counter1->(); # 3
say $counter2->(); # 1
say $counter2->(); # 2
say $counter1->(); # 4
sub { }
捕获作用域内的词法变量,即使它们所在的作用域消失了,也可以让 sub 访问它们。
你每天都在不知不觉中使用这个能力。
my $foo = ...;
sub print_foo { print "$foo\n"; }
如果 subs 没有捕获,上面的代码将不会在模块中工作,因为在调用模块中的任何函数之前通常会退出文件的词法范围。
不仅 print_foo
需要捕获 $foo
才能使上述工作正常,而且在编译时也必须这样做。
sub testUnique {
return !$seen{shift}++;
}
基本相同
BEGIN {
*testUnique = sub {
return !$seen{shift}++;
};
}
这意味着 sub { }
在编译时执行,这意味着它捕获 %seen
在编译时 存在 ,这意味着在循环之前开始了。
循环的第一遍将使用相同的 %seen
,但是将为每个后续遍创建一个新的 %seen
以允许
my @outer;
for (...) {
my @inner = ...;
push @outer, \@inner;
}
如果您在 运行 时执行 sub { }
,就没有问题。
for (0..1){
my %seen;
local *testUnique = sub {
return !$seen{shift}++;
};
my $res = testUnique(1);
if($res){
print "unique\n";
}else{
print "non-unique\n";
}
}
我想用 this 方法从数组中删除重复值。重复删除必须在循环内执行。 这是一个演示我遇到的问题的最小示例:
use strict;
for (0..1){
my %seen;
sub testUnique{
return !$seen{shift}++;
}
my $res = testUnique(1);
if($res){
print "unique\n";
}else{
print "non-unique\n";
}
}
我在循环中定义了 %seen
散列,因此我希望它仅在循环的单次迭代期间定义。
然而,上面代码的结果是:
unique
non-unique
通过一些调试打印,我发现 %seen
的值从一个迭代到另一个迭代被保留。
我尝试了一个简单的
for (0..1){
my %seen;
$seen{1}++;
print "$seen{1}\n";
}
这一个按预期工作。它打印:
1
1
所以,我猜问题出在内部函数 testUnique
上。
有人能给我解释一下这是怎么回事吗?
您的 testUnique
子关闭超过 %seen
的第一个实例。即使它在 for
循环内,子例程也不会重复编译。
您的代码被编译一次,包括 在 for
循环 的顶部初始化词法范围变量 %hash
的部分。 =16=]
以下将产生您想要的输出,但我不确定我看到的是沿着这条路走下去:
#!/usr/bin/env perl
use warnings;
use strict;
for (0..1){
my %seen;
my $tester = sub {
return !$seen{shift}++;
};
print $tester->(1) ? "unique\n" : "not unique\n";
}
子例程只能定义一次,不会为循环的每次迭代重新创建。因此,它只包含对初始 %seen
哈希的引用。添加一些输出有助于澄清这一点:
use strict;
use warnings;
for(0 .. 1) {
my %seen = ();
print "Just created " . \%seen . "\n";
sub testUnique {
print "Testing " . \%seen . "\n";
return ! $seen{shift} ++;
}
if(testUnique(1)) {
print "unique\n";
}
else {
print "non-unique\n";
}
}
输出:
Just created HASH(0x994fc18)
Testing HASH(0x994fc18)
unique
Just created HASH(0x993f048)
Testing HASH(0x994fc18)
non-unique
这里可以看出,初始哈希是唯一被测试的。
欢迎来到闭包的世界。
sub make_closure {
my $counter = 0;
return sub { return ++$counter };
}
my $counter1 = make_closure();
my $counter2 = make_closure();
say $counter1->(); # 1
say $counter1->(); # 2
say $counter1->(); # 3
say $counter2->(); # 1
say $counter2->(); # 2
say $counter1->(); # 4
sub { }
捕获作用域内的词法变量,即使它们所在的作用域消失了,也可以让 sub 访问它们。
你每天都在不知不觉中使用这个能力。
my $foo = ...;
sub print_foo { print "$foo\n"; }
如果 subs 没有捕获,上面的代码将不会在模块中工作,因为在调用模块中的任何函数之前通常会退出文件的词法范围。
不仅 print_foo
需要捕获 $foo
才能使上述工作正常,而且在编译时也必须这样做。
sub testUnique {
return !$seen{shift}++;
}
基本相同
BEGIN {
*testUnique = sub {
return !$seen{shift}++;
};
}
这意味着 sub { }
在编译时执行,这意味着它捕获 %seen
在编译时 存在 ,这意味着在循环之前开始了。
循环的第一遍将使用相同的 %seen
,但是将为每个后续遍创建一个新的 %seen
以允许
my @outer;
for (...) {
my @inner = ...;
push @outer, \@inner;
}
如果您在 运行 时执行 sub { }
,就没有问题。
for (0..1){
my %seen;
local *testUnique = sub {
return !$seen{shift}++;
};
my $res = testUnique(1);
if($res){
print "unique\n";
}else{
print "non-unique\n";
}
}