2012-06-06 35 views
15

我使用each通過一個Perl哈希迭代:我可以在不重置其「每個」迭代器的情況下複製散列嗎?

while (my ($key,$val) = each %hash) { 
    ... 
} 

然後一些有趣的情況,我想打印出來的哈希值。起初,我認爲是這樣的:

while (my ($key,$val) = each %hash) { 
    if (something_interesting_happens()) { 
     foreach my $k (keys %hash) { print "$k => $hash{$k}\n" } 
    } 
} 

但是,這是行不通的,因爲大家都知道,一個散列調用keys(或values)重置用於each內部迭代器,我們可以得到一個無限循環。例如,這些腳本將永遠運行:

perl -e '%a=(foo=>1); while(each %a){keys %a}' 
perl -e '%a=(foo=>1); while(each %a){values %a}' 

沒問題,我想。我可以製作哈希的副本,並打印出副本。

if (something_interesting_happens()) { 
     %hash2 = %hash; 
     foreach my $k (keys %hash2) { print "$k => $hash2{$k}\n" } 
    } 

但這也行不通。這也會重置迭代器each。實際上,在列表上下文中使用%hash似乎會重置其迭代器each。所以這些也永遠運行:

perl -e '%a=(foo=>1); while(each %a){%b = %a}' 
perl -e '%a=(foo=>1); while(each %a){@b = %a}' 
perl -e '%a=(foo=>1); while(each %a){print %a}' 

這是記錄在任何地方嗎?有意義的是,perl可能需要使用相同的內部迭代器來將哈希的內容推送到返回棧中,但我也可以想象不需要這樣做的哈希實現。

更重要的是,有沒有辦法做我想要的?在不重置迭代器的情況下獲得散列的所有元素?


這也表明你不能在each迭代中調試散列。考慮上運行的調試器:

%a = (foo => 123, bar => 456); 
while (($k,$v) = each %a) { 
    $DB::single = 1; 
    $o .= "$k,$v;"; 
} 
print $o; 

只需通過檢查,其中調試器停止(比如打字p %ax %a)哈希,你會改變程序的輸出。


更新:我上傳Hash::SafeKeys作爲一般的解決這個問題。感謝@gpojd指導我正確的方向,@cjm提供了一個讓解決方案更簡單的建議。

+0

最近遇到同樣的問題。發現Hash :: SafeKeys非常慢並且依賴於散列大小,並且Hash :: StoredIterator +默認鍵的性能要好得多。 –

+0

@AlexandrEvstigneev - 謝謝你,這很有趣。我會對'Hash :: SafeKeys'進行一些更新' – mob

回答

9

你試過Storable'sdclone要複製嗎?或許,這將是這樣的:

use Storable qw(dclone); 
my %hash_copy = %{ dclone(\%hash) }; 
+3

'perl -MStorable = dclone -e'%a =(foo => 1); while(each%a){dclone \%a}''不是無限的當我嘗試它時循環。 – cjm

+0

這似乎工作。那麼,Stored :: dclone在做什麼? XS魔法? –

+3

聰明! [Storable'中的''store_hash']函數(http://cpansearch.perl.org/src/AMS/Storable-2.30/Storable.xs)保存了迭代器的狀態,在存儲它時迭代了散列,然後在完成時恢復迭代器狀態。 – mob

2

這個散列有多大?需要多長時間來遍歷它,以便關心訪問的時間?

只需設置一個標誌和迭代結束後做的動作:

my $print_it; 
while (my ($key,$val) = each %hash) { 
    $print_it = 1 if something_interesting_happens(); 
    ... 
} 

if ($print_it) { 
    foreach my $k (keys %hash) { print "$k => $hash{$k}\n" } 
} 

雖然沒有理由不打印輸出代碼中使用each也一樣,除非你打算用鑰匙或排序一些東西。

1

讓我們不要忘記,keys %hash已經定義,當你進入while循環。人們可以簡單地保存鍵進入供以後使用數組:

my @keys = keys %hash; 

while (my ($key,$val) = each %hash) { 

    if (something_interesting_happens()) { 

     print "$_ => $hash{$_}\n" for @keys; 
    } 
} 

缺點:

  • 它是那麼優雅(主觀)
  • 它不會,如果%hash被修改工作(但隨後一個爲什麼會首先使用each

潛在上升空間:?

  • 它避免哈希複製
1

沒有真正使用較少的內存。 each令人難以置信的脆弱。它將迭代狀態存儲在迭代散列本身上,當它們需要時,由Perl的其他部分重用狀態。更安全的是忘記它是否存在,並且始終從keys %hash的結果中迭代您自己的列表,因爲列表上的迭代狀態作爲for循環本身的一部分以詞彙形式存儲,因此免受其他事件的損壞。

相關問題