Array::Each::Overrideなるものを触ってみた

ぼへーっとCPANRSS眺めてたらArray::Each::Overrideとかいうが目に付いたのでさっそく触ってみた。

以下概要のまんまだけど例。

 use Array::Each::Override;
 
 my @array = qw/a b c/;
 while (my ($i,$val) = each @array) {
     print qq{$i:$val\n};
 }

 # 出力結果
 0:a
 1:b
 2:c

その名の通り標準関数eachが配列でも動くようにするためのモジュールですね。

配列をひとつずつ処理しつつ添え字も必要なときに便利ですな、コレ。

今まではそーゆー処理しようと思ったら、

 for my $i (0..$#array) {
     my $val = $array[$i];
     print qq{$i:$val};
 }

とか

 for (my $i=0;$i<@array;$i++) {
     my $val = $array[$i];
     print qq{$i:$val};
 }

といった感じしかなかったんで、見た目的にはなかなか綺麗ですね。まるでPHPのforeachみたい。(まぁ実際はwhileでループするんで違うっちゃ違いますが。)

あと調べてみたらArray::Eachってモジュールもあったんだけどこっちはオブジェクト指向で若干使いにくそうな感じだった。

で、なんとなくBenchmarkでも

 use Benchmark qw(cmpthese :hireswallclock);
 use Array::Each;
 use Array::Each::Override;
 
 my @array = ('a') x 500000;
 
 sub each_foreach {
     for my $i (0..$#array) {
         my $val = $array[$i];
     }
 }
 
 sub each_for {
     for (my $i=0;$i<@array;$i++) {
         my $val = $array[$i];
     }
 }
 
 sub each_object {
     my $one = Array::Each->new( \@array );
     while( my( $val, $i ) = $one->each() ) {
     }
 }
 
 sub each_override {
     while( my($i,$val) = each @array ) {
     }
 }
 
 cmpthese(1, {
     'each_foreach'  => \&each_foreach,
     'each_for'      => \&each_for,
     'each_object'   => \&each_object,
     'each_override' => \&each_override,
 });

以下実行結果

                  Rate   each_object each_override      each_for  each_foreach
 each_object   0.337/s            --          -81%          -90%          -93%
 each_override  1.73/s          414%            --          -49%          -62%
 each_for       3.37/s          900%           95%            --          -26%
 each_foreach   4.57/s         1256%          164%           36%            --

思ったよりもなかなか速い。このモジュール有りかも。ただArray::Eachの方は激遅orz。

ちなみにuseするときに、

 use Array::Each::Override qw/:global/;

ってやるとパッケージ関係なしにeach関数がオーバーライドされます。内部実装としてはCORE::GLOBALを上書きしてますね。

このモジュール使いたいけどもeach関数がオーバーライドされるのは避けたいって人には別名の関数も用意されているので

 use Array::Each::Override qw/array_each/;
 
 my @array = qw/a b c/;
 while (my ($i,$val) = array_each @array) {
     print qq{$i:$val\n};
 }

ってやればOK。

あ、そうそうこのモジュール、eachの他にもkeysやvaluesに対応しちょります。

そーゆーことなんで以下のような処理を書いて、

 use Array::Each::Override;

 my @array = qw/a b c/;
 while (my($i, $val) = each @array) {
     my @idx = keys @array;
 }

無限ループに嵌らないように気をつけましょうネ^-^