我可以在 perl 中调用带有子类比较的超类排序吗? [英] Can I call a superclass sort with a subclass compare in perl?
问题描述
我想使用使用子类比较函数的超类排序.我试图在以下代码中提炼问题的性质.这不是生产"代码,而是在此处提供以供说明.已经测试过了.
#!/usr/bin/perl# $Id: foo,v 1.10 2019/02/23 14:14:33 bennett Exp bennett $使用严格;使用警告;包装水果;使用 Scalar::Util 'blessed';子新{我的 $class = shift;我的 $self = bless({}, $class);$self->{itemList} = [];警告与类一起调用",祝福 $self,\n";返回 $self;}包装苹果;使用父 qw(-norequire Fruit);子排序{我的 $self = shift;@{$self->{itemList}} = 排序比较@{$self->{itemList}};返回 $self;}子比较{$a->{mass} <=>$b->{质量};}包主;我的 $apfel = Apples->new();push(@{$apfel->{itemList}}, { "name" => "grannysmith", "mass" => 12 });push(@{$apfel->{itemList}}, { "name" => "macintosh", "mass" => 6 });push(@{$apfel->{itemList}}, { "name" => "Alkmene", "mass" => 8 });$apfel->mySort();对于我的 $f (@{$apfel->{itemList}}) {printf("%s 是 %d\n", $f->{name}, $f->{mass});}退出 0;
我想要做的是将 mySort()
移动到抽象超类 Fruit
.我已经尝试了多种方法来解决 $self->compare()
子例程,但我运气不佳.
有什么想法吗?
我已经让它调用了正确的子程序,但从来没有使用正确的 $a
和 $b
.我已经把我所有失败的尝试都排除在这个问题之外,希望有人能立即知道如何将 mySort()
移动到 Fruit
包,以便我可以用相同的子程序对我的橙子进行分类.
您有两个问题.首先,您需要超类中的 mySort
函数来为正确的子类调用 compare
函数.其次,您需要子类中的 compare
函数,以便能够从不同包中的调用接收要比较的两个元素.
不清楚你是否找到了第一个问题的解决方案,但一个解决方案是使用UNIVERSAL::can
来找出正确的比较方法.
打包水果;子排序{我的 $self = shift;我的 $compare_func = $self->can("compare");@{$self->{itemList}} = sort $compare_func @{$self->{itemList}};}
这将找到正确的子类 compare
函数并在排序调用中使用它.
现在 Apples::compare
函数中的问题是,当 Fruit::mySort
准备好比较几个元素时,它将设置包变量$Fruit::a
和 $Fruit::b
,而不是 $Apples::a
和 $Apples::b代码>.所以你的
Apples::compare
函数必须为此做好准备.这里有几个解决方案:
包装苹果;子比较{包装水果;$a->{mass} <=>$b->{质量};}
或
子比较{$Fruit::a->{mass} <=>$水果::b->{质量}}
或者更具防御性,
包装苹果;子比较{我的 $pkg = 来电者;如果 ($pkg ne __PACKAGE__) {没有严格的参考";$a = ${"${pkg}::a"};$b = ${"${pkg}::b"};}$a->{mass} <=>$b->{质量}}
<小时>
更新:我考虑过创建一个子程序属性,将 $a
和 $b
值复制到正确的包中,但经过基准测试它并考虑替代方案,我决定反对.这是我对后代的结果:
考虑三个排序例程(可能在另一个包中并且难以从当前包中使用)
sub numsort { $a <=>$b }子词法排序 { $a cmp $b }sub objsort { $a->{value} <=>$b->{value} }
我们可以通过以下一些方法来访问这些包:
实现一个子程序属性以准备正确包中的
$a
和$b
变量.实现太长,无法包含在这里,但子声明看起来像sub numsort : CrossPkg { $a <=>$b }
重写比较函数来比较
$_[0]
和$_[1]
而不是$a
和$b
,并在sort
调用中使用包装器sub lexcmp { $_[0] cmp $_[1] }...@output = sort { lexcmp($a,$b) } @input
在正确的包中执行排序调用,从而设置正确的
$a
和$b
值.@output = do { package OtherPackage;排序 numsort @input };
这里是基准测试结果.local
方法是普通的 sort
调用,没有跨包问题.
总结:lexsort
的开销不是那么大,每次比较都需要更多时间.属性到达时方法已死.设置包进入sort
调用具有最好的结果——或多或少没有开销——但事实并非如此适用于此应用程序(在对象层次结构中).重写比较函数并封装函数在 sort
调用中,性能下降还不算太糟糕,它在对象层次结构中工作,所以最终建议是:
打包水果;子比较{ ... }子排序{我的 $self = shift;@{$self->{itemList}} =sort { $self->can("compare")->($a,$b) } @{$self->{itemList}};}包装苹果;我们的@ISA = qw(水果)子比较 { $_[0]->{mass} <=>$_[1]->{质量}}
I want to use a superclass sort which uses a subclass compare function. I've tried to distill the nature of the question in the following code. This isn't the "production" code, but is presented here for illustration. It's tested.
#!/usr/bin/perl
# $Id: foo,v 1.10 2019/02/23 14:14:33 bennett Exp bennett $
use strict;
use warnings;
package Fruit;
use Scalar::Util 'blessed';
sub new {
my $class = shift;
my $self = bless({}, $class);
$self->{itemList} = [];
warn "Called with class ", blessed $self, "\n";
return $self;
}
package Apples;
use parent qw(-norequire Fruit);
sub mySort {
my $self = shift;
@{$self->{itemList}} = sort compare @{$self->{itemList}};
return $self;
}
sub compare {
$a->{mass} <=> $b->{mass};
}
package main;
my $apfel = Apples->new();
push(@{$apfel->{itemList}}, { "name" => "grannysmith", "mass" => 12 });
push(@{$apfel->{itemList}}, { "name" => "macintosh", "mass" => 6 });
push(@{$apfel->{itemList}}, { "name" => "Alkmene", "mass" => 8 });
$apfel->mySort();
for my $f (@{$apfel->{itemList}}) {
printf("%s is %d\n", $f->{name}, $f->{mass});
}
exit 0;
What I want to do is to move mySort()
to the abstract superclass Fruit
. I've tried a number ways of addressing the $self->compare()
subroutine, but I'm not having much luck.
Any thoughts?
I've gotten it to call the correct subroutine, but never with the correct $a
and $b
. I've left all of my failed attempts out of this question in the hopes that someone will know right away how to move the mySort()
to the Fruit
package so that I can sort my oranges with the same subroutine.
You've got two problems. First, you need the mySort
function in the super class to call the compare
function for the correct subclass. Second, you need the compare
function in the subclass to be able to receive the two elements it wants to compare from a call in a different package.
It's not clear whether you worked out a solution to the first problem, but one solution is to use UNIVERSAL::can
to find out the right comparison method.
package Fruit;
sub mySort {
my $self = shift;
my $compare_func = $self->can("compare");
@{$self->{itemList}} = sort $compare_func @{$self->{itemList}};
}
This will find the correct subclass compare
function and use it in the sort call.
Now the issue in the Apples::compare
function will be that when Fruit::mySort
is ready to compare a couple of elements, it will set the package variables $Fruit::a
and $Fruit::b
, not $Apples::a
and $Apples::b
. So your Apples::compare
function must be prepared for this. Here are a couple of solutions:
package Apples;
sub compare {
package Fruit;
$a->{mass} <=> $b->{mass};
}
or
sub compare {
$Fruit::a->{mass} <=> $Fruit::b->{mass}
}
or more defensively,
package Apples;
sub compare {
my $pkg = caller;
if ($pkg ne __PACKAGE__) {
no strict 'refs';
$a = ${"${pkg}::a"};
$b = ${"${pkg}::b"};
}
$a->{mass} <=> $b->{mass}
}
Update: I thought about making a subroutine attribute that would copy $a
and $b
values into the correct package, but after benchmarking it and thinking about alternatives, I decided against it. Here were my results for posterity:
Consider three sort routines (that might be in another package and hard to use from the current package)
sub numsort { $a <=> $b }
sub lexsort { $a cmp $b }
sub objsort { $a->{value} <=> $b->{value} }
Here are some ways we can make these packages accessible:
implement a subroutine attribute to prepare the
$a
and$b
variables in the right package. Implementation is too long to include here, but the sub declaration would look likesub numsort : CrossPkg { $a <=> $b }
rewrite the comparison function to compare
$_[0]
and$_[1]
instead of$a
and$b
, and use a wrapper in thesort
callsub lexcmp { $_[0] cmp $_[1] } ... @output = sort { lexcmp($a,$b) } @input
Perform the sort call in the correct package, so it sets the correct
$a
and$b
values.@output = do { package OtherPackage; sort numsort @input };
And here are the benchmarking results. The local
method is the ordinary sort
call with no cross-package issues.
Rate attrib-numsort wrap-numcmp local-numsort repkg-numsort attrib-numsort 1.17/s -- -90% -96% -96% wrap-numcmp 11.6/s 885% -- -61% -64% local-numsort 29.5/s 2412% 155% -- -8% repkg-numsort 32.2/s 2639% 178% 9% -- Rate attrib-lexsort repkg-lexsort wrap-lexcmp local-lexsort attrib-lexsort 3.17/s -- -12% -14% -17% repkg-lexsort 3.60/s 13% -- -2% -5% wrap-lexcmp 3.68/s 16% 2% -- -3% local-lexsort 3.80/s 20% 6% 3% -- Rate attrib-objsort wrap-objcmp local-objsort repkg-objsort attrib-objsort 1.22/s -- -81% -88% -89% wrap-objcmp 6.32/s 417% -- -38% -44% local-objsort 10.1/s 730% 61% -- -10% repkg-objsort 11.3/s 824% 79% 11% --
Summary: overhead is less of a concern with lexsort
,
where each comparison takes more time. The attribute
approach is dead on arrival. Setting the package going into
the sort
call has the
best results -- more or less no overhead -- but it isn't
suitable for this application (in an object hierarchy).
Rewriting the comparison function and wrapping the function
in the sort
call isn't too bad of a performance drop-off,
and it works in an object hierarchy, so the final
recommendation is:
package Fruit;
sub compare { ... }
sub mySort {
my $self = shift;
@{$self->{itemList}} =
sort { $self->can("compare")->($a,$b) } @{$self->{itemList}};
}
package Apples;
our @ISA = qw(Fruit)
sub compare { $_[0]->{mass} <=> $_[1]->{mass} }
这篇关于我可以在 perl 中调用带有子类比较的超类排序吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!