2010-09-03 102 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