鈍足ランナーのIT日記

走るのが好きな5流のITエンジニアのブログ。

趣味の範囲は広いけど、どれも中途半端なクソブロガー楽しめるWebアプリを作ってあっと言わせたい。サーバーサイドPerl(Mojolicious)、クライアントサイドVue.js。Arduinoにも触手を伸ばす予定。

perlで配列の引き算、再調査

1年前にも配列の引き算(Diffrence)を考えていたけれど。

http://dokechin.hatenablog.com/entry/2013/07/03/213407

f:id:kechiya:20140804070829p:plain

tsucchiさんのブログにコストに関する記述が興味深い。 http://d.hatena.ne.jp/tsucchi1022/20090419/1240152657

my @array1 = (1,2,3,4,5);
my @array2 = (3,4,5,6,7);

@union = @intersection = @difference = ();
%count = ();
foreach $element (@array1, @array2) { $count{$element}++ }
foreach $element (keys %count) {
    push @union, $element;
    push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element;
}

print "@union\n";
print "@intersection\n";
print "@difference\n";

ただ、このソースでの"difference"はオレンジ部分と青部分の和なので、 differenceとは違くて そこで、AとBのintersection(緑)を求めて Aと緑の"difference"を求めるとオレンジ部分(A-B)になる。 こんな感じです。

use strict;
use warnings;

my @array1 = (1,2,3,4,5);
my @array2 = (3,4,5,6,7);

my @minus = array_minus(\@array1,\@array2);

print "@minus";

sub array_minus{

  my ($array1_ref, $array2_ref) = @_;

  my @array1 = @$array1_ref;
  my @array2 = @$array2_ref;

  my @union1 = my @intersection1 = my @difference1 = ();
  my %count = ();
  foreach my $element (@array1, @array2) { $count{$element}++ }
  foreach my $element (keys %count) {
      push @union1, $element;
      push @{ $count{$element} > 1 ? \@intersection1 : \@difference1 }, $element;
  }

  my @union2 = my @intersection2 = my @difference2 = ();
  %count = ();
  foreach my $element (@array1, @intersection1) { $count{$element}++ }
  foreach my $element (keys %count) {
      push @union2, $element;
      push @{ $count{$element} > 1 ? \@intersection2 : \@difference2 }, $element;
  }

  return @difference2;

}

私的にはよく使うので、ArrayのDifference(差)ができるCPANモジュールをご存じの方は教えて欲しいな・・

追記 papixさんにArray::Diffを教えていただきました。

さて、どうせ負けるだろうと、いざ、ベンチ勝負!

use strict;
use warnings;
use Benchmark qw/timethese cmpthese/;
use Array::Diff;

my @array1 = (1..10000);
my @array2 = (2..9999);

my $result = timethese(100, {
  array_minus => sub {
    my @minus = array_minus(\@array1,\@array2);
  },
  array_diff => sub {
    my $diff = Array::Diff->diff( \@array1, \@array2 );
  },
});

cmpthese $result;
              Rate  array_diff array_minus
array_diff  11.1/s          --        -41%
array_minus 18.9/s         70%          --

Array::Diffは増分も検知するから、比較してもあまり意味がないのですが、マイナスだけの演算に特化している分速いのかな。

さらに追記 ArrayあたりをキーワードにCPANを漁ると Acme::Toolsにminusという関数があり、これも私の求めている関数なんですが。。 ベンチした結果に驚きを隠せない。(マシンが先ほどのベンチとは違いますが)この性能差はかなりのもの。。

              Rate  array_diff array_minus  acme_tools
array_diff  1.16/s          --        -49%        -84%
array_minus 2.26/s         94%          --        -68%
acme_tools  7.12/s        513%        216%          --

Acme::Tools恐るべし・・

追記

Array::Utilsを発見した。 これは、Acme::Toolsと方式が似ている感じ。(ハッシュにして比較)