web-dev-qa-db-ja.com

Perlの配列(リスト)に値が存在することを確認するにはどうすればよいですか?

可能な値のリストがあります:

@a = qw(foo bar baz);

$val@aに存在するか、存在しないかを簡潔に確認するにはどうすればよいですか?

明らかな実装はリストをループすることですが、私は確信しています [〜#〜] tmtowtdi [〜#〜]


答えてくれたすべての人に感謝します!私が強調したい3つの答えは次のとおりです。

  1. 受け入れられた答え-最も「組み込み」で下位互換性のある方法。

  2. RETの答え が最もクリーンですが、Perl5.10以降にのみ適しています。

  3. draegtunの答え は(おそらく)少し高速ですが、追加のモジュールを使用する必要があります。依存関係を回避できる場合は、依存関係を追加するのは好きではありません。この場合、パフォーマンスの違いは必要ありませんが、1,000,000要素のリストがある場合は、この回答を試してみてください。

20
MaxVT

Perlのgrep()関数のbulitは、これを行うように設計されています。

@matches = grep( /^MyItem$/, @someArray ); 

または、任意の式をマッチャーに挿入できます

@matches = grep( $_ == $val, @a ); 
20
Cheekysoft

Perl 5.10を使用している場合は、 スマートマッチ演算子 ~~を使用します。

print "Exist\n" if $var ~~ @array;

それはほとんど魔法です。

38
RET

これは、 perlfaq4「特定の要素がリストまたは配列に含まれているかどうかを確認するにはどうすればよいですか?」 に対する回答で回答されます。

Perlfaqを検索するには、お気に入りのブラウザを使用して、 perlfaq のすべての質問のリストを検索できます。

コマンドラインから、perldocへの-qスイッチを使用してキーワードを検索できます。あなたは「リスト」を検索することによってあなたの答えを見つけたでしょう:

perldoc -q list

(この回答の一部は、AnnoSiegelとbriand foyによって提供されました)

「in」という単語が聞こえるということは、データを格納するために、リストや配列ではなくハッシュを使用する必要があったことを示しています。ハッシュは、この質問に迅速かつ効率的に答えるように設計されています。配列はそうではありません。

そうは言っても、これに取り組むにはいくつかの方法があります。 Perl 5.10以降では、スマートマッチ演算子を使用して、アイテムが配列またはハッシュに含まれていることを確認できます。

use 5.010;

if( $item ~~ @array )
    {
    say "The array contains $item"
    }

if( $item ~~ %hash )
    {
    say "The hash contains $item"
    }

以前のバージョンのPerlでは、もう少し作業を行う必要があります。任意の文字列値に対してこのクエリを何度も実行する場合、最も速い方法は、おそらく元の配列を反転し、キーが最初の配列の値であるハッシュを維持することです。

@blues = qw/Azure cerulean teal turquoise lapis-lazuli/;
%is_blue = ();
for (@blues) { $is_blue{$_} = 1 }

これで、$ is_blue {$ some_color}かどうかを確認できます。そもそもブルースをすべてハッシュにしておくのは良い考えだったかもしれません。

値がすべて小さい整数の場合は、単純なインデックス付き配列を使用できます。この種の配列は、より少ないスペースを占有します。

@primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31);
@is_tiny_prime = ();
for (@primes) { $is_tiny_prime[$_] = 1 }
# or simply  @istiny_prime[@primes] = (1) x @primes;

ここで、$ is_tiny_prime [$ some_number]かどうかを確認します。

問題の値が文字列ではなく整数である場合、代わりにビット文字列を使用することで、かなりのスペースを節約できます。

@articles = ( 1..10, 150..2000, 2017 );
undef $read;
for (@articles) { vec($read,$_,1) = 1 }

ここで、vec($ read、$ n、1)がいくつかの$ nに対して真であるかどうかを確認します。

これらのメソッドは、高速な個別テストを保証しますが、元のリストまたは配列を再編成する必要があります。同じ配列に対して複数の値をテストする必要がある場合にのみ、効果があります。

テストを1回だけ行う場合、標準モジュールList :: Utilはこの目的のために最初に関数をエクスポートします。要素が見つかると停止することで機能します。速度を上げるためにCで記述されており、Perlに相当するものは次のサブルーチンのようになります。

sub first (&@) {
    my $code = shift;
    foreach (@_) {
        return $_ if &{$code}();
    }
    undef;
}

速度がほとんど問題にならない場合、一般的なイディオムは、スカラーコンテキスト(条件を通過したアイテムの数を返す)でgrepを使用して、リスト全体をトラバースします。ただし、これには、見つかった一致の数を通知するという利点があります。

my $is_there = grep $_ eq $whatever, @array;

一致する要素を実際に抽出する場合は、リストコンテキストでgrepを使用するだけです。

my @matches = grep $_ eq $whatever, @array;
18
brian d foy

Perlに標準で付属している List :: Utilfirst関数を使用します。

use List::Util qw/first/;

my @a = qw(foo bar baz);
if ( first { $_ eq 'bar' } @a ) { say "Found bar!" }

注意。 firstは、最初に見つかった要素を返すため、完全なリストを反復処理する必要はありません(これがです) grepで十分です)。

15
draegtun

考えられるアプローチの1つは、List :: MoreUtils'any '関数を使用することです。

use List::MoreUtils qw/any/;

my @array = qw(foo bar baz);

print "Exist\n" if any {($_ eq "foo")} @array;

更新:zoulのコメントに基づいて修正されました。

8
neversaint

特に繰り返し検索する場合の興味深い解決策:

my %hash;
map { $hash{$_}++ } @a;
print $hash{$val};
5
zoul
$ Perl -e '@a = qw(foo bar baz);$val="bar";
if (grep{$_ eq $val} @a) {
  print "found"
} else {
  print "not found"
}'

見つかった

$val='baq';

見つかりません

2
mfontani

不要な依存関係が気に入らない場合は、anyまたはfirstを自分で実装してください

sub first (&@) {
  my $code = shift;
  $code->() and return $_ foreach @_;
  undef
}

sub any (&@) {
  my $code = shift;
  $code->() and return 1 foreach @_;
  undef
}