15

私はeachPerlハッシュを反復処理するために使用しています:

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(または)を呼び出すと、に使用される内部イテレータがリセットされることを誰もが知っているため、無限ループが発生する可能性があります。たとえば、これらのスクリプトは永久に実行されます。valueseach

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イテレータをリセットせずにハッシュのすべての要素にアクセスするには?


eachこれは、イテレーション内でハッシュをデバッグできないことも示唆しています。次の場所でデバッガーを実行することを検討してください。

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

デバッガーが停止するハッシュを検査するだけで(たとえば、p %aまたはを入力x %a)、プログラムの出力を変更します。


更新:Hash::SafeKeysこの問題の一般的な解決策としてアップロードしました。私を正しい方向に向けてくれた@gpojdと、解決策をはるかに簡単にする提案をしてくれた@cjmに感謝します。

4

4 に答える 4

9

Storable dcloneをコピーしてみましたか?おそらく次のようになります。

use Storable qw(dclone);
my %hash_copy = %{ dclone( \%hash ) };
于 2012-06-06T20:16:28.703 に答える
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キーなどで並べ替える場合を除いて、印刷コードで使用しない理由もありません。

于 2012-06-06T20:07:37.360 に答える
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そもそもなぜ使用するのでしょうか?)

利点:

  • ハッシュコピーを回避することにより、メモリの使用量を削減します
于 2012-06-07T06:13:41.800 に答える
1

あまり。each信じられないほど壊れやすいです。反復状態を反復ハッシュ自体に格納します。この状態は、必要なときにperlの他の部分によって再利用されます。keys %hashリストの反復状態はループ自体の一部として字句的に格納されるforため、他のものによる破損の影響を受けないため、存在することを忘れて、代わりに常に結果から独自のリストを反復する方がはるかに安全です。

于 2012-06-17T15:03:54.593 に答える