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

Wrap perl's sort function in an object

我正在尝试在我的一个(面向对象的)包中提供一个排序函数,它接受一个块并像标准 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

大部分。

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

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

$a$b 是声明排序子例程的包的全局变量(假设这是 Some::Caller)。当 运行 在您的 class 中时,排序将设置 $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 不会让你这样做。