Perl变量范围问题

Perl变量范围问题,perl,class,scope,Perl,Class,Scope,所以我有一个Perl类。它有一个sort()方法,我希望它或多或少与内置的sort()函数相同: $object->sort(sub ($$) { $_[0] <=> $_[1] }); sub sample (&@) $object->sort(子($$){$\[0]$\[1]}); 但我做不到: $object->sort(sub { $a <=> $b }); $object->sort(sub{$a$b}); 因为范围界定。但是Lis

所以我有一个Perl类。它有一个
sort()
方法,我希望它或多或少与内置的
sort()
函数相同:

$object->sort(sub ($$) { $_[0] <=> $_[1] });
sub sample (&@)
$object->sort(子($$){$\[0]$\[1]});
但我做不到:

$object->sort(sub { $a <=> $b });
$object->sort(sub{$a$b});
因为范围界定。但是List::Util模块使用
reduce()
来实现这一点。我查看了List::Util模块,他们使用
没有严格的“vars”
来做一些非常糟糕的事情。我试过了,但没用

我的理解是,
reduce()
的工作方式是这样的,因为它被导出到适当的名称空间中,因此我的类不能这样做,因为函数固定在另一个名称空间中。这是正确的,还是在我的情况下有某种(无疑是更可怕和更不明智的)方法可以做到这一点?

您可以使用设置子例程调用期间
$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);
然后你这样称呼它:

sample { print "$a $b\n" } qw(a b c d e f g h i j);
但是,方法不受原型的影响。

您可以使用它来查找与coderef关联的包(它称之为
stash_name
)。然后根据需要在该包中设置$a和$b。您可能需要在方法中使用
无严格的“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 });
使用严格;
使用警告;
包装食品;
使用Sub::标识“隐藏名称”;
子分类{
我的($self,$sub)=@;
我的$pkg=stash_name($sub);
my@x=qw(1 6 39 2 5);
打印“@x\n”;
{
没有严格的“参考文献”;
@x=排序{
本地(${$pkg.'::a'},${$pkg.::b'})=($a,$b);
$sub->();
}@x;
}
打印“@x\n”;
返回;
}
包装分拣机;
子比较{$a$b}
主包装;
严格使用;
使用警告;
我的$foo={};
祝福$foo,‘foo’;
$foo->sort(\&Sorter::compare);
$foo->sort(sub{$b$a});

其他两个答案都对了一半。下面是一个实际排序的有效解决方案:

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
package-Foo;
严格使用;
使用警告;
子分类{
我的($self,$sub)=@;
我的($pkg)=来电者;
my@x=qw(1 6 39 2 5);
打印“@x\n”;
{
没有严格的“参考文献”;
@x=排序{
本地(${$pkg.'::a'},${$pkg.::b'})=($a,$b);
$sub->();
}@x;
}
打印“@x\n”;
返回;
}
主包装;
严格使用;
使用警告;
我的$foo={};
祝福$foo,‘foo’;
$foo->sort(sub{$a$b});
# 1 6 39 2 5
# 1 2 5 6 39
您可能会对一些实际上是对象一部分的数据进行排序

您需要这种魔力,以便在调用程序包中本地化
$a
$b
,Perl将在其中查找它们。它创建的全局变量只在调用子对象时存在


请注意,您将获得一个带有
警告的“名称仅使用一次”
;我相信你可以通过一些手段来避免这种情况。

他特别询问的是一种
方法,而不是普通的sub。如果你在课堂外调用该方法,这是行不通的。您正在本地化类的$a和$b,而不是调用方的。我以为我看到了,但我想没有。perlvar给我的印象是,
$a
$b
非常神奇,它们在这种情况下“正常工作”。不,$a和$b只是普通的包变量。@Rob:是的,我也是这么想的——但当我检查时,
perlvar
实际上说它们是包变量。List::Util只使用
调用者
。我想这在一般情况下是不够的,是吗?如果调用者从其他包传递函数,则List::Util将设置调用者的
$a
,而不是函数的。5.10.1中的List::Util版本使用XS代码,基本上与Sub::Identify执行相同的操作来确定coderef所属的包。或者,我可以在XS中重写我的模块,这样就失去了依赖性,给了我学习XS的机会。不过,在此期间,我会调查一下。这对你来说可能足够好了,但它很脆弱。无法保证比较函数与
sort
方法的调用者属于同一个包。这就是Sub::Identify的用武之地。@cjm-这是真的,我肯定会研究Sub::Identify,但我更大的问题是如何让它工作,而不是让它在一般情况下工作。具体的解决方案比一般的失败要好。然而,将这个答案与您的答案结合起来,会给我一个通用的解决方案,这是一件好事。尽管
排序
内置也有同样的问题。它假定比较函数与调用者来自同一个包。因此,如果您可以接受这一点,那么就可以在Sub::Identify上保存一个依赖项。(或者你可以有条件地要求Sub::identification,如果没有安装的话就退回到
调用者那里去。但这需要更多的工作。)事实上,这正是我一直在考虑的,我想我会去做的。啊哈,我从来没有想过。虽然我不确定这样的事情是否应该得到鼓励。