配列を反復処理せずに、配列内の値の存在を確認する方法を理解しようとしています。
パラメータのファイルを読み取っています。処理したくないパラメーターの長いリストがあります。これらの不要なパラメータを配列に配置しました@badparams
。
新しいパラメーターを読み取り、それがに存在しない場合は@badparams
処理します。に存在する場合は@badparams
、次の読み取りに進みます。
配列を反復処理せずに、配列内の値の存在を確認する方法を理解しようとしています。
パラメータのファイルを読み取っています。処理したくないパラメーターの長いリストがあります。これらの不要なパラメータを配列に配置しました@badparams
。
新しいパラメーターを読み取り、それがに存在しない場合は@badparams
処理します。に存在する場合は@badparams
、次の読み取りに進みます。
回答:
配列をハッシュに変換するだけです。
my %params = map { $_ => 1 } @badparams;
if(exists($params{$someparam})) { ... }
リストに(一意の)パラメータをさらに追加することもできます。
$params{$newparam} = 1;
そして、後で(一意の)パラメータのリストを取得します。
@badparams = keys %params;
1
再び設定されます。
最高の汎用性-特に短い配列(1000アイテム以下)と、どの最適化が自分のニーズに最適であるかわからないコーダー。
# $value can be any regex. be safe
if ( grep( /^$value$/, @array ) ) {
print "found it";
}
配列の最初の値が一致する場合でも、grepはすべての値を通過することが言及されています。これは事実ですが、ほとんどの場合、grepは非常に高速です。短い配列(1000項目未満)について話している場合、ほとんどのアルゴリズムはとにかくかなり高速になります。非常に長い配列(1,000,000項目)について話している場合、grepは、項目が配列の最初か中間か最後かに関係なく、許容範囲内で高速です。
より長いアレイの最適化ケース:
配列がソートされている場合は、「バイナリ検索」を使用してください。
同じ配列が何度も繰り返し検索される場合は、まず配列をハッシュにコピーしてから、ハッシュを確認します。メモリが問題になる場合は、各項目を配列からハッシュに移動します。メモリ効率は向上しますが、元の配列が破壊されます。
場合は、同じ値が繰り返し検索され、アレイ内、なまけキャッシュを構築します。(各アイテムが検索されるとき、最初に検索結果が永続的なハッシュに格納されているかどうかを確認します。検索結果がハッシュに見つからない場合は、配列を検索し、結果を永続的なハッシュに入れて、次回はハッシュでそれを見つけ、検索をスキップします)。
注:これらの最適化は、長い配列を処理する場合にのみ高速になります。過度に最適化しないでください。
次のように、Perl 5.10で smartmatch機能を使用できます。
リテラル値の検索については、以下を実行するとうまくいきます。
if ( "value" ~~ @array )
スカラー検索の場合、以下を実行すると上記と同様に機能します。
if ($val ~~ @array)
以下を行うインライン配列の場合、上記のように動作します。
if ( $var ~~ ['bar', 'value', 'foo'] )
Perl 5.18実験としてフラグが立てられているスマートマッチしたがって、あなたはをオンにして警告をオフにする必要があり、実験スクリプト/モジュールに下記追加することにより、プラグマ:
use experimental 'smartmatch';
または、smartmatchの使用を避けたい場合は、Aaronが次のように使用します。
if ( grep( /^$value$/, @array ) ) {
#TODO:
}
このブログ投稿では、この質問に対する最良の回答について説明しています。
簡単にまとめると、CPANモジュールをインストールできる場合、最も読みやすいソリューションは次のとおりです。
any(@ingredients) eq 'flour';
または
@ingredients->contains('flour');
ただし、より一般的なイディオムは次のとおりです。
any { $_ eq 'flour' } @ingredients
ただし、このfirst()
機能は使用しないでください。コードの意図をまったく表現していません。~~
「スマートマッチ」演算子は使用しないでください。壊れています。またgrep()
、ハッシュを使用したソリューションも使用しないでください。リスト全体を繰り返し処理します。
any()
それはあなたの価値を見つけるとすぐに停止します。
詳細については、ブログ投稿をご覧ください。
grep
リソースを調べる場合は、の使用を避けてください。
if ( grep( /^$value$/, @badparams ) ) {
print "found";
}
for (@badparams) {
if ($_ eq $value) {
print "found";
last;
}
}
my %hash = map {$_ => 1} @badparams;
print "found" if (exists $hash{$value});
(Perl 5.10で追加され、マークはPerl 5.18では実験的です)。
use experimental 'smartmatch'; # for perl 5.18
print "found" if ($value ~~ @badparams);
List::MoreUtils
use List::MoreUtils qw(any);
@badparams = (1,2,3);
$value = 1;
print "found" if any {$_ == $value} @badparams;
@eakssjoのベンチマークが壊れている-ループでハッシュを作成する方法とループで正規表現を作成する方法。修正バージョン(および私が追加したList::Util::first
とList::MoreUtils::any
):
use List::Util qw(first);
use List::MoreUtils qw(any);
use Benchmark;
my @list = ( 1..10_000 );
my $hit = 5_000;
my $hit_regex = qr/^$hit$/; # precompute regex
my %params;
$params{$_} = 1 for @list; # precompute hash
timethese(
100_000, {
'any' => sub {
die unless ( any { $hit_regex } @list );
},
'first' => sub {
die unless ( first { $hit_regex } @list );
},
'grep' => sub {
die unless ( grep { $hit_regex } @list );
},
'hash' => sub {
die unless ( $params{$hit} );
},
});
そして結果(@eakssjoの回答よりも10倍多い100_000回の反復に対するものです):
Benchmark: timing 100000 iterations of any, first, grep, hash...
any: 0 wallclock secs ( 0.67 usr + 0.00 sys = 0.67 CPU) @ 149253.73/s (n=100000)
first: 1 wallclock secs ( 0.63 usr + 0.01 sys = 0.64 CPU) @ 156250.00/s (n=100000)
grep: 42 wallclock secs (41.95 usr + 0.08 sys = 42.03 CPU) @ 2379.25/s (n=100000)
hash: 0 wallclock secs ( 0.01 usr + 0.00 sys = 0.01 CPU) @ 10000000.00/s (n=100000)
(warning: too few iterations for a reliable count)
使用するのは便利ですが、ハッシュに変換するソリューションにはかなりのコストがかかるようです。これは私にとって問題でした。
#!/usr/bin/perl
use Benchmark;
my @list;
for (1..10_000) {
push @list, $_;
}
timethese(10000, {
'grep' => sub {
if ( grep(/^5000$/o, @list) ) {
# code
}
},
'hash' => sub {
my %params = map { $_ => 1 } @list;
if ( exists($params{5000}) ) {
# code
}
},
});
ベンチマークテストの出力:
Benchmark: timing 10000 iterations of grep, hash...
grep: 8 wallclock secs ( 7.95 usr + 0.00 sys = 7.95 CPU) @ 1257.86/s (n=10000)
hash: 50 wallclock secs (49.68 usr + 0.01 sys = 49.69 CPU) @ 201.25/s (n=10000)
List::Util::first
一致が見つかると反復を停止するため、使用はより高速です。
これを行うには、2つの方法があります。他の投稿で提案されているように、ルックアップテーブルのハッシュに値をスローすることができます。(もう1つのイディオムを追加します。)
my %bad_param_lookup;
@bad_param_lookup{ @bad_params } = ( 1 ) x @bad_params;
しかし、ほとんどが単語文字でメタが多すぎないデータの場合は、正規表現の代替にダンプできます。
use English qw<$LIST_SEPARATOR>;
my $regex_str = do {
local $LIST_SEPARATOR = '|';
"(?:@bad_params)";
};
# $front_delim and $back_delim being any characters that come before and after.
my $regex = qr/$front_delim$regex_str$back_delim/;
このソリューションは、探している「不良値」のタイプに合わせて調整する必要があります。繰り返しになりますが、特定のタイプのストリングにはまったく不適切な場合があるため、emptorを警告します。
@bad_param_lookup{@bad_params} = ()
、を使用exists
してメンバーシップをテストする必要があります。
my @badparams = (1,2,5,7,'a','zzz');
my $badparams = join('|',@badparams); # '|' or any other character not present in params
foreach my $par (4,5,6,7,'a','z','zzz')
{
if ($badparams =~ /\b$par\b/)
{
print "$par is present\n";
}
else
{
print "$par is not present\n";
}
}
数値の先行スペースの一貫性を確認することができます