SimpleBoxes

[perl]ハッシュ(連想配列)のマージ

perlのハッシュ(連想配列)のマージについてちょっと調べました。自分用のメモとして記録しておきます。

一番直観的な方法は以下のようなコードでしょう。

%hash = (%hash, %addition);

ハッシュ %hash に対して、%addition の内容を追加します。

my %hash = (
  'key1' => 'value1',
  'key2' => 'value2',
);
my %addition = (
  'key3' => 'value3',
  'key4' => 'value4',
  'key1' => 'override1',
);
%hash = (%hash, %addition);

上述のようなコードを実行すると、%hash の内容は以下のようになります。

  'key1' => 'override1',
  'key2' => 'value2',
  'key3' => 'value3',
  'key4' => 'value4',

key1 の内容が上書きされているのがポイント。

whileforeach でループを回してハッシュをマージする方法も利用できます。

whileeach を組み合わせると、以下のようなコードになります。

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

foreachkeys を組み合わせて、以下のような方法でもマージできます。

foreach my $key (keys %addition)
{
  $hash{$key} = $addition{$key};
}

同様の操作は map を使っても可能です。

map { $hash{$_} = $addition{$_} } keys %addition;

最後にスライスを使う方法もあります。スライスを使い慣れた人には、ぱっと書けると思うのですが、私にはすぐには書けませんでした。

@hash { keys %addition } = values %addition;

%hash ではなく、@hash と記述します。

ハッシュのリファレンスを利用した場合も同様に記述できます。

# normal merge
$hash = {%{$hash}, %addition};
# use while
while (my($key,$val) = each (%addition))
{
  $hash->{$key} = $val;
}
# use foreach
foreach my $key (keys %addition)
{
  $hash->{$key} = $addition{$key};
}
# use map
map { $hash->{$_} = $addition{$_} } keys %addition;
# use slice
@{$hash} { keys %addition } = values %addition;

速度がどの程度違うのか、調べてみました。

利用したコードは「続き」に記載しています。

foreach:  4 wallclock secs ( 4.17 usr +  0.00 sys =  4.17 CPU) @ 119904.08/s (n=500000)
    map:  5 wallclock secs ( 3.96 usr +  0.00 sys =  3.96 CPU) @ 126262.63/s (n=500000)
  merge:  7 wallclock secs ( 6.66 usr +  0.01 sys =  6.67 CPU) @ 74962.52/s (n=500000)
  slice:  5 wallclock secs ( 4.32 usr +  0.01 sys =  4.33 CPU) @ 115473.44/s (n=500000)
  while:  5 wallclock secs ( 4.35 usr +  0.01 sys =  4.36 CPU) @ 114678.90/s (n=500000)

使うなら、map でしょうか。

スライスを利用した方法も、マージするハッシュの内容によって速くなる場合がありましたが、具体的な条件が分かりませんでした。

従って、手元の簡単な調査では map によるマージが比較的安定した結果を出すという印象です。

use Benchmark;
timethese(
  500000,
  {
    'merge' => sub {
      my %hash = (
        'key1' => 'value1',
        'key2' => 'value2',
      );
      my %addition = (
        'key3' => 'value3',
        'key4' => 'value4',
        'key1' => 'override1',
      );
      %hash = (%hash, %addition);
    },
    'while' => sub {
      my %hash = (
        'key1' => 'value1',
        'key2' => 'value2',
      );
      my %addition = (
        'key3' => 'value3',
        'key4' => 'value4',
        'key1' => 'override1',
      );
      while (my($key,$val) = each (%addition))
      {
        $hash{$key} = $val;
      }
    },
    'foreach' => sub {
      my %hash = (
        'key1' => 'value1',
        'key2' => 'value2',
      );
      my %addition = (
        'key3' => 'value3',
        'key4' => 'value4',
        'key1' => 'override1',
      );
      foreach my $key (keys %addition)
      {
        $hash{$key} = $addition{$key};
      }
    },
    'map' => sub {
      my %hash = (
        'key1' => 'value1',
        'key2' => 'value2',
      );
      my %addition = (
        'key3' => 'value3',
        'key4' => 'value4',
        'key1' => 'override1',
      );
      map { $hash{$_} = $addition{$_} } keys %addition;
    },
    'slice' => sub {
      my %hash = (
        'key1' => 'value1',
        'key2' => 'value2',
      );
      my %addition = (
        'key3' => 'value3',
        'key4' => 'value4',
        'key1' => 'override1',
      );
      @hash { keys %addition } = values %addition;
    },
  }
);

スポンサーリンク

<< 記事評価スクリプトの概要 :: WebKit で CodePress を利用するために >>