21

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

@a = qw(foo bar baz);

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

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


答えてくれてありがとう!私が強調したい3つの答えは次のとおりです。

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

  2. RET の回答は最もクリーンですが、Perl 5.10 以降でのみ有効です。

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

4

8 に答える 8

18

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

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

コマンドラインから -q スイッチを使用して perldoc にキーワードを検索できます。「リスト」を検索すると、答えが見つかります。

perldoc -q list

(この回答の一部はAnno Siegelとbrian d 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 }

$n に対して vec($read,$n,1) が true かどうかを確認します。

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

一度だけテストする場合、標準モジュール 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;
于 2009-04-06T12:50:51.117 に答える
8

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

use List::MoreUtils qw/any/;

my @array = qw(foo bar baz);

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

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

于 2009-04-06T07:34:54.060 に答える
2
$ perl -e '@a = qw(foo bar baz);$val="bar";
if (grep{$_ eq $val} @a) {
  print "found"
} else {
  print "not found"
}'

見つかった

$val='baq';

見つかりません

于 2009-04-06T08:26:00.927 に答える
1

不要な依存関係が嫌なら実装anyfirst自分で

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

sub any (&@) {
  my $code = shift;
  $code->() and return 1 foreach @_;
  undef
}
于 2009-04-07T19:11:56.933 に答える