web-dev-qa-db-ja.com

Perl配列に特定の値が含まれているかどうかをどうやって確認できますか?

配列を繰り返し処理せずに、配列内に値が存在するかどうかを調べる方法を見つけようとしています。

パラメータ用のファイルを読んでいます。対処したくないパラメータがたくさんあります。これらの不要なパラメータを配列@badparamsに配置しました。

新しいパラメーターを読みたいのですが、それが@badparamsに存在しない場合はそれを処理します。 @badparamsに存在する場合は、次の読み取りに進みます。

218
Mel

配列をハッシュに変えるだけです。

my %params = map { $_ => 1 } @badparams;

if(exists($params{$someparam})) { ... }

リストにもっと(ユニークな)パラメータを追加することもできます。

$params{$newparam} = 1;

そして後で(一意の)パラメータの一覧を取得します。

@badparams = keys %params;
177
jkramer

最も一般的な目的 - 特に短い配列(1000項目以下)や、最適化が自分のニーズに最も適しているかどうかわからないコーダー。

# $value can be any regex. be safe
if ( grep( /^$value$/, @array ) ) {
  print "found it";
}

たとえ配列の最初の値が一致しても、grepはすべての値を通過すると述べられています。これは本当です、しかし grepは、ほとんどの場合、まだ非常に高速です。。短い配列(1000アイテム未満)について話しているのであれば、ほとんどのアルゴリズムはとにかくかなり高速になるでしょう。非常に長い配列(1,000,000項目)について話している場合、項目が配列の最初か中央か最後かに関係なく、grepは容認できるほど速いです。

長い配列の最適化の場合

配列がソートされている場合、 "バイナリ検索"を使用してください。

あれば 同じ配列が繰り返し検索されます 何度も、最初にそれをハッシュにコピーしてからハッシュをチェックしてください。メモリが問題になる場合は、各項目を配列からハッシュに移動します。メモリ効率は向上しますが、元の配列が破壊されます。

もし 同じ値が繰り返し検索されます 配列内で、遅延キャッシュを構築します。 (各項目が検索されるとき、検索結果が持続的なハッシュで保存されているかどうかを最初に確認します。検索結果がハッシュに見つからない場合は、配列を検索して結果を持続的なハッシュに入れます。ハッシュで見つけて検索をスキップしてください。

注:これらの最適化は、長い配列を扱う場合に限り速くなります。最適化しすぎないでください。

208
Aaron T Harris

次のようにPerl 5.10でスマートマッチ機能を使用できます。

文字通りの値検索のためには、以下のようにすればうまくいきます。

if ( "value" ~~ @array ) 

スカラールックアップの場合、以下を実行すると上記と同じように機能します。

if ($val ~~ @array)

以下のインライン配列の場合は、上記のように動作します。

if ( $var ~~ ['bar', 'value', 'foo'] ) 

Perl 5.18smartmatchでは実験的なフラグが立てられているため、実験的に をオンにして警告をオフにする必要があります スクリプト/モジュールに下記を追加してプラグマ:

use experimental 'smartmatch';

代わりにスマートマッチの使用を避けたい場合 - アーロンが使用すると言ったように:

if ( grep( /^$value$/, @array ) ) {
  #TODO:
}
116
Bitmap

このブログ投稿 では、この質問に対する最良の回答について説明しています。

簡単に言えば、CPANモジュールをインストールできるのであれば、最も読みやすい解決策は次のとおりです。

any(@ingredients) eq 'flour';

または

@ingredients->contains('flour');

ただし、より一般的な慣用句は次のとおりです。

any { $_ eq 'flour' } @ingredients

しかし、first()関数を使わないでください。それはあなたのコードの意図をまったく表現していません。 ~~の "Smart match"演算子を使用しないでください。それは壊れています。 grep()やハッシュを使った解法は使わないでください。それらはリスト全体を反復処理します。

any()はあなたの価値が見つかるとすぐに停止します。

詳細についてはブログ投稿をチェックしてください。

42
mascip

使用するのは便利ですが、ハッシュへの変換ソリューションにはかなりのパフォーマンスが必要になるようです。これは私にとって問題でした。

#!/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)
11
aksel

@ eakssjoのベンチマーク が壊れています - ループ内でハッシュを作成するのとループ内で正規表現を作成するのを比較しています。修正版(さらにList::Util::firstList::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} );
        },
    });

そしてその結果(100,000回の繰り返しで、@ eakssjoの答えよりも10倍多い):

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)
10
Xaerxess

方法1:grep(値が正規表現になると予想される間は注意が必要です)。

リソースを見る場合は、grepを使用しないようにしてください。

if ( grep( /^$value$/, @badparams ) ) {
  print "found";
}

方法2:線形検索

for (@badparams) {
    if ($_ eq $value) {
       print "found";
    }
}

方法3:ハッシュを使う

my %hash = map {$_ => 1} @badparams;
print "found" if (exists $hash{$value});

方法4:スマートマッチ

(Perl 5.10で追加、マークはPerl 5.18で実験的なものです)。

use experimental 'smartmatch';  # for Perl 5.18
print "found" if ($value ~~ @badparams);

方法5:コアモジュールList::MoreUtilsを使用する

use List::MoreUtils qw(any uniq);;
@badparams = (1,2,3);
$value = 1;
print "found" if any {$_ eq $value} @badparams;
5
Kamal Nayan

あなたは確かにここにハッシュが欲しいのです。悪いパラメータをキーとしてハッシュ内に配置してから、ハッシュ内に特定のパラメータが存在するかどうかを判断します。

our %bad_params = map { $_ => 1 } qw(badparam1 badparam2 badparam3)

if ($bad_params{$new_param}) {
  print "That is a bad parameter\n";
}

あなたが本当にそれを配列で行うことに興味があるならば、List::UtilまたはList::MoreUtilsを見てください。

2
David M

@filesは既存の配列です

my @new_values =  grep(/^2[\d].[\d][A-za-z]?/,@files);

print join("\n", @new_values);

print "\n";

/^2[\d].[\d][A-za-z]?/ = 2から始まる値ここでは、任意の正規表現を置くことができます

2
Rohan

これを行うには2つの方法があります。他の投稿で示唆されているように、ルックアップテーブルのハッシュに値を投げるを使うことができます。 (もう1つ慣用句を追加します。)

my %bad_param_lookup;
@bad_param_lookup{ @bad_params } = ( 1 ) x @bad_params;

しかし、それがたいていWordの文字のデータで、あまりにも多くのメタのデータではない場合は、それを正規表現の代替にダンプできます。

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/;

この解決策は、探している「悪い値」の種類に合わせて調整する必要があります。繰り返しになりますが、特定の種類の文字列ではまったく不適切な場合があるため、警告記号を使用します。

0
Axeman