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