2010-09-03 61 views
9

不想排序条目。基于插入顺序迭代散列?

使用这种不保留顺序以及

foreach my $val (keys %hash) { 
    ... 
} 
+2

散列由散列码存储,顾名思义。如果他们不是,他们不会那么快。 – 2010-09-03 18:50:12

+3

如果你想要这个,你应该使用我认为对的列表。 – alternative 2010-09-03 18:54:03

回答

20

哈希是无序的,默认情况下在Perl 5可以使用tieTie::IxHash重写此行为。不过要注意的是,有性能问题和其他考虑因素(比如订单不会保存在副本中)。

#!/usr/bin/perl 

use strict; 
use warnings; 

use Tie::IxHash; 

tie my %hash, "Tie::IxHash" 
    or die "could not tie %hash"; 

$hash{one} = 1; 
$hash{two} = 2; 
$hash{three} = 3; 

for my $k (keys %hash) { 
    print "$k $hash{$k}\n"; 
} 

一个更好的选择可能是使用数组或哈希散列:

#!/usr/bin/perl 

use strict; 
use warnings; 

my %hash; 
$hash{one} = { data => 1, order => 1 }; 
$hash{three} = { data => 3, order => 2 }; 
$hash{two} = { data => 2, order => 3 }; 

for my $k (sort { $hash{$a}{order} <=> $hash{$b}{order} } keys %hash) { 
    print "$k $hash{$k}{data}\n"; 
} 

至于性能方面,这里有一个基准的结果:

IndexedOO: a, b, c, d, e, f 
HashOrdered: a, b, c, d, e, f 
IxHashOO: a, b, c, d, e, f 
hash: f, e, a, c, b, d 
hoh_pis: a, b, c, d, e, f 
IxHash: a, b, c, d, e, f 
hoh: a, b, c, d, e, f 
Indexed: a, b, c, d, e, f 
       Rate IxHash hoh Indexed IxHashOO IndexedOO hoh_pis HashOrdered hash 
IxHash  261/s  -- -18% -26%  -48%  -54% -57%  -66% -80% 
hoh   316/s 21% -- -10%  -37%  -44% -48%  -59% -75% 
Indexed  353/s 35% 12%  --  -29%  -38% -42%  -55% -72% 
IxHashOO  499/s 91% 58%  41%  --  -12% -18%  -36% -61% 
IndexedOO 569/s 118% 80%  61%  14%  --  -7%  -27% -56% 
hoh_pis  611/s 134% 93%  73%  22%  7%  --  -21% -52% 
HashOrdered 778/s 198% 146% 120%  56%  37%  27%   -- -39% 
hash  1279/s 391% 305% 262%  156%  125% 109%   64% -- 

从这我们可以看到,如果你不需要它像一个正常的散列(即一个并列散列)那样使用Hash :: Ordered就是要走的路。

这里是基准:

#!/usr/bin/perl 

use strict; 
use warnings; 

use Tie::IxHash; 
use Tie::Hash::Indexed; 
use Hash::Ordered; 
use Benchmark; 

#this is O(n) instead of O(n log n) or worse 
sub perfect_insert_sort { 
    my $h = shift; 
    my @k; 
    for my $k (keys %$h) { 
     $k[$h->{$k}{order}] = $k; 
    } 
    return @k; 
} 

my @keys = qw/a b c d e f/; 
my %subs = (
    hash => sub { 
     my $i; 
     my %h = map { $_ => $i++ } @keys; 
     return join ", ", keys %h; 
    }, 
    hoh => sub { 
     my $i; 
     my $order; 
     my %h = map { 
      $_ => { data => $i++, order => $order++ } 
     } @keys; 
     return join ", ", sort { $h{$a}{order} <=> $h{$b}{order} } 
      keys %h; 
    }, 
    hoh_pis => sub { 
     my $i; 
     my $order; 
     my %h = map { 
      $_ => { data => $i++, order => $order++ } 
     } @keys; 
     return join ", ", perfect_insert_sort \%h; 
    }, 
    IxHash => sub { 
     my $i; 
     tie my %h, "Tie::IxHash"; 
     %h = map { $_ => $i++ } @keys; 
     return join ", ", keys %h; 
    }, 
    Indexed => sub { 
     my $i; 
     tie my %h, "Tie::Hash::Indexed"; 
     %h = map { $_ => $i++ } @keys; 
     return join ", ", keys %h; 
    }, 
    IxHashOO => sub { 
     my $i; 
     my $o = tie my %h, "Tie::IxHash", 
      map { $_ => $i++ } @keys; 
     return join ", ", $o->Keys; 
    }, 
    IndexedOO => sub { 
     my $i; 
     my $o = tie my %h, "Tie::Hash::Indexed", 
      map { $_ => $i++ } @keys; 
     my @k = my $k = $o->FIRSTKEY; 
     while ($k = $o->NEXTKEY($k)) { 
      push @k, $k; 
     } 
     return join ", ", @k; 
    }, 
    HashOrdered => sub { 
    my $i; 
     my $oh = Hash::Ordered->new(map { $_ => $i++ } @keys); 
     return join ", ", $oh->keys; 
    }, 
); 

for my $sub (keys %subs) { 
    print "$sub: ", $subs{$sub}(), "\n"; 
} 

@keys = 1 .. 1_000; 
Benchmark::cmpthese -2, \%subs; 
+0

['Tie :: Hash :: Indexed'](http://p3rl.org/Tie::Hash::Indexed)比IxHash快。尽管这种联系机制本身就是两种解决方案的大幅放缓。 – daxim 2010-09-04 12:30:10

+0

@daxim我达到了我记得的第一个。我不认为我熟悉'Tie :: Hash :: Indexed',我将不得不看一看。 – 2010-09-04 13:30:37

+1

搭售机制本身就消耗了很多性能。为了得到它,你可以直接使用绑定对象,你知道它将成为一个并列哈希。 '$ obj = tied%hash;例如$ obj-> STORE($ key,$ value)'。完整的接口在Tie :: Hash文档中。 – Schwern 2010-09-04 19:38:21