我希望使用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
。
有人可以解释一下这里发生了什么吗?
答案 0 :(得分:4)
您的testUnique
子关闭%seen
的第一个实例。即使它在for
循环内,子程序也不会重复编译。
您的代码编译一次,包括在%hash
循环顶部初始化词法范围变量for
的部分。
以下将产生您想要的输出,但我不确定我是否会看到这条路:
#!/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";
}
答案 1 :(得分:3)
子程序只能定义一次,并且不会为循环的每次迭代重新创建。因此,它只保留对初始%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
在这里可以看出,初始哈希是唯一被测试的哈希值。
答案 2 :(得分:2)
欢迎来到封闭世界。
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 { }
捕获范围内的词汇变量,即使它们存在的范围消失,也可以对它们进行子访问。
你每天都在不知情的情况下使用这种能力。
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";
}
}