111

(キー、値) ペアの束を持つ Perl ハッシュがある場合、すべてのキーを反復処理するための推奨される方法は何ですか? eachを使用すると、何らかの形で意図しない副作用が生じる可能性があると聞いたことがあります。それで、それは本当ですか、次の2つの方法のいずれかが最善ですか、それともより良い方法がありますか?

# Method 1
while (my ($key, $value) = each(%hash)) {
    # Something
}

# Method 2
foreach my $key (keys(%hash)) {
    # Something
}
4

9 に答える 9

212

経験則としては、ニーズに最も適した機能を使用することです。

キーだけが必要で、値を読み取る予定がない場合は、keys() を使用します。

foreach my $key (keys %hash) { ... }

値だけが必要な場合は、values() を使用します。

foreach my $val (values %hash) { ... }

キーと値が必要な場合は、each() を使用します。

keys %hash; # reset the internal iterator so a prior each() doesn't affect the loop
while(my($k, $v) = each %hash) { ... }

反復中に現在のキーを削除する以外の方法でハッシュのキーを変更する場合は、 each() を使用しないでください。たとえば、2 倍の値を持つ大文字のキーの新しいセットを作成する次のコードは、keys() を使用して正常に動作します。

%h = (a => 1, b => 2);

foreach my $k (keys %h)
{
  $h{uc $k} = $h{$k} * 2;
}

予想される結果のハッシュを生成します。

(a => 1, A => 2, b => 2, B => 4)

ただし、 each() を使用して同じことを行います。

%h = (a => 1, b => 2);

keys %h;
while(my($k, $v) = each %h)
{
  $h{uc $k} = $h{$k} * 2; # BAD IDEA!
}

予測しにくい方法で誤った結果を生成します。例えば:

(a => 1, A => 2, b => 2, B => 8)

ただし、これは安全です。

keys %h;
while(my($k, $v) = each %h)
{
  if(...)
  {
    delete $h{$k}; # This is safe
  }
}

これらはすべて、perl のドキュメントで説明されています。

% perldoc -f keys
% perldoc -f each
于 2008-08-06T13:22:14.620 に答える
28

使用するときに注意する必要があることの1つeachは、ハッシュに「状態」を追加するという副作用があることです(ハッシュは「次の」キーが何であるかを覚えておく必要があります)。上記のスニペットのようにハッシュ全体を一度に繰り返すコードを使用する場合、これは通常問題にはなりません。ただし、すべてのキーを処理する前に、またはループを終了するeachなどのステートメントと一緒 に使用すると、問題を追跡するのが困難になります(私は経験から話します;) 。lastreturnwhile ... each

この場合、ハッシュはすでに返されたキーを記憶しておりeach、次回(おそらくまったく関係のないコードで)使用するときに、この位置で続行されます。

例:

my %hash = ( foo => 1, bar => 2, baz => 3, quux => 4 );

# find key 'baz'
while ( my ($k, $v) = each %hash ) {
    print "found key $k\n";
    last if $k eq 'baz'; # found it!
}

# later ...

print "the hash contains:\n";

# iterate over all keys:
while ( my ($k, $v) = each %hash ) {
    print "$k => $v\n";
}

これは印刷します:

found key bar
found key baz
the hash contains:
quux => 4
foo => 1

キー「bar」とbazはどうなりましたか?それらはまだありますが、2番目のキーeachは最初のキーが中断したところから始まり、ハッシュの終わりに達すると停止するため、2番目のループでそれらを確認することはできません。

于 2008-09-15T23:37:14.490 に答える
21

each問題を引き起こす可能性のある場所は、それが真のスコープのないイテレータであることです。例として:

while ( my ($key,$val) = each %a_hash ) {
    print "$key => $val\n";
    last if $val; #exits loop when $val is true
}

# but "each" hasn't reset!!
while ( my ($key,$val) = each %a_hash ) {
    # continues where the last loop left off
    print "$key => $val\n";
}

eachがすべてのキーと値を確実に取得する必要がある場合は、最初にkeysorを使用する必要がありますvalues(反復子がリセットされるため)。それぞれのドキュメントを参照してください。

于 2008-09-16T14:35:43.583 に答える
14

each 構文を使用すると、キーのセット全体が一度に生成されなくなります。これは、数百万行のデータベースに関連付けられたハッシュを使用している場合に重要です。キーのリスト全体を一度に生成して、物理メモリを使い果たしたくはありません。この場合、それぞれが反復子として機能しますが、キーは実際にはループが開始する前に配列全体を生成します。

したがって、「それぞれ」が実際に使用されるのは、(使用可能なメモリと比較して) ハッシュが非常に大きい場合のみです。これは、ハンドヘルド データ収集デバイスまたは小さなメモリを備えたものをプログラミングしていない限り、ハッシュ自体がメモリ自体に存在しない場合にのみ発生する可能性があります。

メモリが問題にならない場合は、通常、マップまたはキーのパラダイムがより普及しており、読みやすいパラダイムです。

于 2008-09-11T22:04:22.110 に答える
6

このトピックに関するいくつかの雑多な考え:

  1. ハッシュ反復子自体に安全でないものは何もありません。安全でないのは、反復処理中にハッシュのキーを変更することです。(値を変更することは完全に安全です。) 私が考えることができる唯一の潜在的な副作用は、valuesエイリアスを返すことです。つまり、エイリアスを変更すると、ハッシュの内容が変更されます。これは仕様によるものですが、状況によっては希望どおりにならない場合があります。
  2. John の受け入れられた回答は、1 つの例外を除いて適切です。ドキュメントでは、ハッシュを繰り返し処理しているときにキーを追加するのは安全ではないことが明らかです。一部のデータセットでは機能する可能性がありますが、ハッシュ順序によっては他のデータセットでは機能しません。
  3. すでに述べたように、 によって返された最後のキーを削除しても安全eachです。リストを返す間、これはイテレータには当てはまりません。keyseachkeys
于 2008-09-15T21:36:12.300 に答える
3

これに噛まれるかもしれませんが、個人的な好みだと思います。キー()またはvalues()とは異なるeach()への参照をドキュメントで見つけることができません(明白な「それらは異なるものを返す」という答えを除いて。実際、ドキュメントは同じイテレータを使用すると述べており、それらはすべてそれらのコピーではなく実際のリスト値を返します。呼び出しを使用して反復しながらハッシュを変更するのは良くありません。

とはいえ、ほとんどの場合、keys()を使用します。これは、通常、ハッシュ自体を介してキーの値にアクセスする方が自己文書化されているためです。値が大きな構造体への参照であり、ハッシュへのキーがすでに構造体に格納されている場合、values()を使用することがあります。その時点で、キーは冗長であり、必要ありません。私はPerlプログラミングの10年間でeach()を2回使用したと思いますが、どちらの場合もおそらく間違った選択でした=)

于 2008-08-06T03:43:58.563 に答える
3

私はいつも方法2も使います。それぞれを使用する唯一の利点は、ハッシュエントリの値を(再割り当てするのではなく)読み取っているだけの場合、ハッシュを常に逆参照しているわけではないことです。

于 2008-08-06T04:04:16.353 に答える
2

私は普段使っkeysていますが、最後に使ったときや、の使い方を読んだときのことは考えられませんeach

mapループで何をしているかにもよりますが、忘れないでください!

map { print "$_ => $hash{$_}\n" } keys %hash;
于 2008-08-22T15:31:20.770 に答える
-1

私はこう言います:

  1. ほとんどの人にとって最も読みやすい/理解しやすいものを使用してください(したがって、通常、キーは私が主張します)
  2. コードベース全体を通して一貫して決定したものを使用してください。

これにより、次の 2 つの大きな利点が得られます。

  1. 関数/メソッドにリファクタリングできるように、「一般的な」コードを見つけやすくなります。
  2. 将来の開発者が保守しやすくなります。

それぞれにキーを使用する方がコストがかかるとは思わないので、コード内の同じものに対して 2 つの異なる構造を使用する必要はありません。

于 2010-12-20T12:46:21.640 に答える