如何以任意顺序对Perl列表进行排序?

时间:2009-12-08 02:46:15

标签: perl algorithm

我有一个字符串列表,其值来自固定集。我需要对此进行排序 以任意顺序列出。

集合的顺序由所有可能字符串的另一个列表指定,按数组顺序排序。

以下是一个例子:

my @all_possible_strings_in_order = ('name', 'street', 'city','state', 'postalcode');

my @list_that_needs_to_be_sorted = ('city', 'state', 'name');

我在perl工作。我认为最好的办法是自动创建一个将字符串与序数相关联的哈希值,然后通过引用这些序数进行排序。

集合中有大约300个可能的字符串。典型列表将包含30个需要排序的字符串。这不会在紧密的循环中调用,但它也不会很慢。由于程序的结构,无法提前自动构建序数散列。

我愿意就更好的方法提出建议。谢谢!

编辑:你们真棒。我今晚不能再抬起头了,但是明天早上我会花时间真正理解你的建议......这是我精通map()和grep()的时候了。

7 个答案:

答案 0 :(得分:10)

使用

设置字符串与各自位置之间的关联
# Define your custom order of all possible strings.
my @custom_order = qw/ name street city state postalcode /;

my %order = map +($custom_order[$_] => $_), 0 .. $#custom_order;

现在,您可以创建一个比较函数,以便与Perl的sort运算符一起使用:

sub by_order { $order{$a} <=> $order{$b} }

例如:

my @sorted = sort by_order qw/ city state name /;
print "@sorted\n";
# prints: name city state

答案 1 :(得分:7)

一种不同的方法(如果要排序的列表可能包含需要保留的重复项,则该方法不起作用):

my %set;
@set{ @list_that_needs_to_be_sorted } = ();
my @sorted = grep exists $set{$_}, @all_possible_strings_in_order;

答案 2 :(得分:2)

如果你有Perl 5.10,你可以使用它(为了清晰起见缩短名称):

use feature 'state';

sub bylist {
  state %hash = map { $all_possible[$_] => $_ } 0 .. $#all_possible;
  $hash{$_[0]} cmp $hash{$_[1]};
}

my @sorted = sort bylist @list_to_sort;

state关键字在C中创建的内容称为static变量 - 它是bylist子例程的本地变量,但不会重新初始化。这样,您不必事先设置任何内容,但每次要使用它时都不必重新计算值。

我相信在旧的Perls中会发生这种情况,但我不会使用它。如果你没有5.10,只需使用gbacon's这个想法,当我输入这个时,他无耻地偷走了我的大脑:P

答案 3 :(得分:2)

您可以浏览主列表并将未排序列表中出现的任何元素推送到结果列表,同时将其从未排序列表中删除。如果您的未排序列表很短(从您的示例中,我估计大约有5个元素),这应该比每次构建哈希表更快更小(您说您事先不能这样做)。

优化可能是从未排序的列表中创建一个trie,但这是否更好取决于每个列表的大小。

答案 4 :(得分:1)

这是一个相当简单的想法。

从未排序的列表中取出第一个字符串,在主列表中搜索它,在主列表中找到它的索引,然后将其放在列表中,并跟踪索引。

拿第二个字符串,在主列表中找到它的索引。如果该索引大于第一个索引,则将其放在第一个索引后面的新列表中,否则放在前面。

保留所有剩余字符串,维护所有索引的列表,以便始终知道下一个字符串的位置是已经排序的字符串。

希望这很清楚,可以提供帮助。

John Doner

答案 5 :(得分:1)

最天真的方法是基于比较函数进行排序,其中比较函数comp(a,b)=“a和b中哪一个在主列表中首先出现?”,如果我理解正确的话

所以是的,你的想法看起来是正确的。如果您必须在更改@all_possible_strings_in_order之间进行大量排序,那么您应该构建一次整个地图。如果订单列表每种类型都有所改变,您可以通过一些聪明的懒惰搜索获得一些速度,但可能不会。


my %order;
my $i = 0;
foreach my $s (@all_possible_strings_in_order) {
    $order{$s} = $i++;
}

my @sorted = sort {$order{$a} <=> $order{$b}} @list_that_needs_to_be_sorted;

我想这应该很快。

答案 6 :(得分:0)

Sort::ByExample使这很容易,并且让你指定回退排序,以防意外的值最终出现在你的列表中。为了简单起见,我会在这里省略后备。

use Sort::ByExample qw( sbe );

my @all_possible_strings_in_order
    = ( 'name', 'street', 'city', 'state', 'postalcode' );

my @list_that_needs_to_be_sorted = ( 'city', 'state', 'name' );
my $sorter = sbe( \@all_possible_strings_in_order );

my @sorted = $sorter->( @list_that_needs_to_be_sorted );