Perl 变量范围问题
Posted
技术标签:
【中文标题】Perl 变量范围问题【英文标题】:Perl variable scope question 【发布时间】:2010-11-22 11:04:06 【问题描述】:所以我有一个 Perl 类。它有一个sort()
方法,我希望它或多或少与内置的sort()
函数相同:
$object->sort(sub ($$) $_[0] <=> $_[1] );
但我做不到:
$object->sort(sub $a <=> $b );
因为范围。但是 List::Util 模块使用reduce()
执行此操作。我查看了 List::Util 模块,他们使用no strict 'vars'
做了一些相当讨厌的事情来实现这一点。我试过了,但没有用。
据我了解,reduce()
以它的方式工作,因为它被导出到适当的命名空间中,因此我的类不能这样做,因为该函数非常牢固地位于另一个命名空间中。这是正确的,还是在我的情况下有一些(无疑更可怕和不明智的)方法可以做到这一点?
【问题讨论】:
【参考方案1】:嗯,其他两个答案都对了一半。这是一个实际排序的有效解决方案:
package Foo;
use strict;
use warnings;
sub sort
my ($self, $sub) = @_;
my ($pkg) = caller;
my @x = qw(1 6 39 2 5);
print "@x\n";
no strict 'refs';
@x = sort
local ($$pkg.'::a', $$pkg.'::b') = ($a, $b);
$sub->();
@x;
print "@x\n";
return;
package main;
use strict;
use warnings;
my $foo = ;
bless $foo, 'Foo';
$foo->sort(sub $a <=> $b );
# 1 6 39 2 5
# 1 2 5 6 39
大概你会对一些实际上是对象一部分的数据进行排序。
您需要caller
魔法,以便在调用者的包中本地化$a
和$b
,这是Perl 将在其中寻找它们的地方。它正在创建仅在调用 sub 时才存在的全局变量。
请注意,warnings
将获得一个“仅使用一次的名称”;我敢肯定,您可以通过某种方式来避免这种情况。
【讨论】:
这对于您的目的可能已经足够了,但它很脆弱。不能保证比较函数与sort
方法的调用者属于同一个包。这就是 Sub::Identify 的用武之地。
@cjm - 这是真的,我肯定会研究 Sub::Identify,但我更大的问题是让它工作,而不是让它在一般情况下工作。具体解决方案优于一般故障。但是,将此答案与您的答案结合起来会给我一个通用的解决方案,这是一件好事。
虽然事实证明 sort
内置有同样的问题。它假定比较函数来自与调用者相同的包。因此,如果您可以忍受,您可以保存对 Sub::Identify 的依赖。 (或者您可以有条件地要求 Sub::Identify,如果未安装,则回退到 caller
。但这需要更多工作。)
其实这正是我一直在考虑的,我想我会去做的。
啊哈,我从来没想过。虽然我不确定是否应该鼓励这样的事情。【参考方案2】:
您可以使用Sub::Identify 找出与coderef 关联的包(它称为stash_name
)。然后根据需要在该包中设置 $a 和 $b。您可能需要在您的方法中使用 no strict 'refs'
才能使其正常工作。
这是修改后的 Evee 的答案以适用于一般情况:
use strict;
use warnings;
package Foo;
use Sub::Identify 'stash_name';
sub sort
my ($self, $sub) = @_;
my $pkg = stash_name($sub);
my @x = qw(1 6 39 2 5);
print "@x\n";
no strict 'refs';
@x = sort
local ($$pkg.'::a', $$pkg.'::b') = ($a, $b);
$sub->();
@x;
print "@x\n";
return;
package Sorter;
sub compare $a <=> $b
package main;
use strict;
use warnings;
my $foo = ;
bless $foo, 'Foo';
$foo->sort(\&Sorter::compare );
$foo->sort(sub $b <=> $a );
【讨论】:
List::Util 只使用caller
。我想这还不够,在一般情况下,是吗?如果调用者从其他包传递一个函数,那么 List::Util 将设置调用者的$a
而不是函数的。
5.10.1 中的 List::Util 版本使用 XS 代码,基本上与 Sub::Identify 做相同的事情来找出 coderef 所属的包。
另外,我可以在 XS 中重写我的模块,从而失去依赖并且让我有机会学习 XS。不过,与此同时,我会调查一下。【参考方案3】:
您可以使用the local
operator 在子例程调用期间设置$a
和$b
的值:
sub sample
my $callback = shift;
for (my $i = 0; $i < @_; $i += 2)
local ($a, $b) = @_[$i, $i + 1];
$callback->();
sample sub print "$a $b\n" , qw(a b c d e f g h i j);
如果你有一个普通的子程序,而不是一个方法,那么你可以让它更像sort
,所以你不需要在你的回调函数之前使用sub
。在函数上使用原型:
sub sample (&@)
那你这样称呼它:
sample print "$a $b\n" qw(a b c d e f g h i j);
不过,方法不受原型的影响。
【讨论】:
他专门询问method
,而不是普通的子。
如果您从类外调用该方法,那将不起作用。您正在本地化 类的 $a 和 $b,而不是调用者的。
啊。我以为我已经看到了这个,但我想没有。我对 perlvar 的印象是 $a
和 $b
足够神奇,它们在这种情况下“正常工作”。
不,$a 和 $b 只是普通的包变量。
@Rob:是的,我也是这么想的——但是当我检查时,perlvar
实际上确实说它们是包变量。以上是关于Perl 变量范围问题的主要内容,如果未能解决你的问题,请参考以下文章
您好!想请问您一个关于perl安装路径及系统环境变量的问题。