List::Util::firstは遅い
(追記:この用途ではList::Util::firstを使うのは誤りで,List::MoreUtils::anyが意図されたコードです。効率についての結論は変わりません)
List::Util::first{expr}は組み込みのgrep{expr}に似ているが,exprが最初に真になった段階でその値を返すので,grep{expr}よりも効率がいいと説明されることが多い。しかし,実際にベンチマークを取ってみると,多くの場合grep{expr}より遅い。最初の要素が真になるというfirst{expr}にとって最適な条件でさえ,要素数が40を越えたあたりでようやくgrep{expr}とほぼ同程度の速度になる。したがって,List::Util::first{expr}が効果的なケースはそれほど多くないと思われる。
また,もし単なる文字列の検索でよく,その検索をプログラム中で繰り返すならば,grep{expr}よりもハッシュを見るほうが常に圧倒的に速い。
ベンチマーク:
$ perl first_benchmark.pl 40 Perl 5.8.5 on linux. Found in the frist element: Rate grep first hash grep 11377/s -- -9% -96% first 12444/s 9% -- -95% hash 262044/s 2203% 2006% -- Not found: Rate first grep hash first 4483/s -- -61% -99% grep 11487/s 156% -- -96% hash 309688/s 6807% 2596% --
ベンチマークスクリプト(最初の引数で要素数を指定できる。デフォルトは10):
#!perl -w use strict; use Benchmark qw(:all); use List::Util qw(first); printf "Perl %vd on %s.\n", $^V, $^O; my $n = shift @ARGV; my @values = ('foobar', ('xxxxxx') x ($n || 10)); my %values_in; @values_in{@values} = (); print "Found in the frist element:\n"; my $s = $values[0]; cmpthese -1, { grep => sub{ for( 1 .. 10 ){ 1 if grep { $_ eq $s } @values; } }, first => sub{ for( 1 .. 10 ){ 1 if first { $_ eq $s } @values; } }, hash => sub{ for( 1 .. 10){ 1 if exists $values_in{ $s }; } }, }; print "Not found:\n"; $s = reverse $s; cmpthese -1, { grep => sub{ for( 1 .. 10 ){ 1 if grep { $_ eq $s } @values; } }, first => sub{ for( 1 .. 10 ){ 1 if first { $_ eq $s } @values; } }, hash => sub{ for( 1 .. 10){ 1 if exists $values_in{ $s }; } }, }; __END__