2009-12-08 88 views
5

我有一个字符串的列表,其值来自一个固定的集合。我需要按任意顺序对此列表进行排序。如何以任意顺序对Perl列表进行排序?

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

下面是一个例子:

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()的时候了。

+0

您的示例显示“@all_possible_strings_in_order”,但您随后说“由于程序的结构,无法提前完成序号哈希的自动生成”。你可以解释吗?我敢肯定,下面的一些算法可能会被重新调整,以重建散列错过,但怎么可能取决于“程序结构”。 ;) – zen 2009-12-08 04:37:39

+0

该程序反复运行,每次运行时都必须从头构建数据结构。它适合于一个更大的生态系统。我想可以采取一些措施来使其持续下去,但有更高的优先事项。 – NXT 2009-12-08 04:50:38

+0

使用长度为10个字符的300个密钥的Xeon [email protected]的快速基准测试:7325/s使用散列片,3065/s使用映射。这是守护进程的限制,冷启动会根据负载降低20-30%或更多。 – zen 2009-12-08 05:20:47

回答

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 
+4

'my%order; @order {@all_possible_strings_in_order} = 1 .. @ all_possible_strings_in_order' - 少一点邪恶/多一点可读性? :) – hobbs 2009-12-08 03:34:19

+0

我更喜欢'地图'版本(虽然有卷曲),但在另一个问题中,我对这两者进行了剖析,发现切片分配稍微快一些(重复一个常数值)。 – 2009-12-08 03:36:19

+0

(另外,在我的版本中,我相信我们需要'map'版本来初始化'state'变量,但是我不确定) – 2009-12-08 03:38:27

1

这是一个非常简单的想法。

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

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

保留所有剩余的字符串,维护所有索引的列表,以便您始终知道将下一个字符串放置在哪里,重新排序已排序的字符串。

希望这是明确的足以帮助。

约翰·多纳

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子例程,但不会重新初始化。这样,您不必事先设置任何东西,但不必每次重新计算值就可以使用它。

我相信在老版本的Perl中有这种情况发生,但我不会使用它。如果你没有5.10,只需使用gbacon's想法,他无耻地偷了我的大脑,而我打字这一点:P

+1

“hack” - 你永远不应该做,并且不再在Perl 5.10中工作 - 是'my%hash if 0; %散列或%散列= ...' – ephemient 2009-12-08 05:36:19

+1

非黑客解决方法是将子声明放在一个块中,并在那里定义状态变量。例如'{my $ x; sub foo {$ x || = 1; ...}}' – 2009-12-08 13:50:01

+0

甚至'{my%hash = ...; sub bylist {...}}“,即使”bylist“从不输入,它也会预先计算。如果你不得不使用pre-5.10 Perl,那么这是最值得推荐的,并且仍然可以在5.10中工作。另一方面,这可能不是克里斯想要的“黑客”:) – ephemient 2009-12-08 14:39:59

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; 

我想这应该是相当快的。

+0

你的Perl需要一点工作。 (@list)'foreach my $ s?这是什么意思? – 2009-12-08 03:37:08

+0

在3分钟内得到4个答案是正常的,45分钟后没有答案?我感觉像一个perl noob不使用map或范围运算符来生成订单散列。 – 2009-12-08 03:41:31

+2

@Chris:这意味着我一直在下午使用bash。 – 2009-12-08 03:44:35

6

一种不同的方法(之一,如果要排序的列表可以有需要保留复制将无法工作):

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

你可以去接管主列表,将任何元素发生在结果列表的未排序列表中,同时将它从未排序列表中删除。如果你的未排序列表很短(从你的例子来看,我估计大约有5个元素),这应该比每次构建散列表更快更小(你说你事先不能做到这一点)。

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

+0

这和ysth的答案一样。 – 2009-12-09 05:30:39

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);