在对象中包装perl的排序函数

时间:2015-01-21 03:29:13

标签: perl sorting block

我试图在我的一个(面向对象的)包中提供一个排序函数,它接受一个块,并使标准Perl sort可用$ a和$ b。

首先,我在包含包装排序函数的包中尝试做的简化版本:

# In package My::Object
sub sort {
  my $self = shift;
  my $block = \&{shift @_};

  return sort $block @{$self->{arrayRef}}; # I want to use the passed in block with array data contained in this object
}

然后是一个客户端传递一个块的例子,该块定义了为比赛运行的比较器:

my $obj = My::Object->new([3, 1, 5, 6, 2, 4]); # As an example, these values will be come arrayRef from above
my @sortedVals = $obj->sort({ $a < $b });

有没有办法在我能够使用Perl sort的同时做我想做的事情?

1 个答案:

答案 0 :(得分:8)

晴。

要使用bare-block-as-subroutine语法,您需要使用& prototype。通常你应该避免使用原型,但是将子程序作为裸块传递是可接受的少数几次之一。不幸的是,因为它们必须在编译时确定并应用,所以原型不适用于方法。因此,您必须使用完整的匿名子例程语法sub { ... }

my @sortedVals = $obj->sort(sub { $a <=> $b });

$a$b是声明sort子例程的包的全局变量(让我们说这是Some::Caller)。在课程中运行时,排序会设置$My::Object::a$My::Object::b,但子例程会查找$Some::Caller::a$Some::Caller::b。您可以通过将$a$b别名化到$a$b来解决此问题。

sub sort {
    my $self = shift;
    my $block = shift;

    no strict 'refs';
    local *{caller.'::a'} = *a;
    local *{caller.'::b'} = *b;

    return sort $block @{$self->{arrayRef}};
}

local在块的持续时间内生成全局的临时副本,这包括调用的其他子例程,因此这将影响排序。然后,当方法完成后,该值将恢复为原来的状态。

Perl全局变量存储在typeglobs中,其中包含具有相同名称的所有全局变量。 *a包含$a@a以及%a。通过复制来电者的typeglob,当$My::Object::a发生变化时,来电者的$a也会发生变化。它们是别名。

*{...}语法允许您使用另一个变量或表达式按名称获取全局变量。这称为symbolic reference。现在我们可以联系到来电者*a。使用这种语法通常是一个错误,所以你必须关闭strict否则Perl不会让你这么做。