Perl变量范围问题

时间:2009-08-24 23:55:45

标签: perl class scope

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

$object->sort(sub ($$) { $_[0] <=> $_[1] });

但我不能这样做:

$object->sort(sub { $a <=> $b });

由于范围界定。但是List :: Util模块使用reduce()执行此操作。我查看了List :: Util模块,他们用no strict 'vars'做了一些相当讨厌的事情来实现这一点。我试过了,但没有用。

我的理解是reduce()按照它的方式工作,因为它被导出到适当的命名空间,因此我的类不能这样做,因为该函数在另一个命名空间中非常牢固。这是正确的,还是有一些(无疑是更可怕和不明智的)方式在我的情况下这样做?

3 个答案:

答案 0 :(得分:9)

嗯,其他两个答案都是半对的。这是一个实际解决的工作解决方案:

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将要查找它们的地方。它正在创建仅在调用该子时存在的全局变量。

请注意,您将使用warnings获得“仅使用一次”的名称;不管怎样,我确信你可以通过一些箍来避免这种情况。

答案 1 :(得分:4)

您可以使用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 });

答案 2 :(得分:1)

您可以使用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);

但是,方法不受原型的影响。