web-dev-qa-db-ja.com

Perl:if(リスト内の要素)

リスト内の要素の存在を探しています。

Pythonにはinキーワードがあり、次のようなことをします。

if element in list:
    doTask

リスト全体を手動で繰り返すことなく、Perlに同等のものがありますか?

68
Jonathan

更新:

機能のsmartmatchファミリーは現在実験的です

V5.10.0で追加され、v5.10.1で大幅に改訂されたスマートマッチは、不満の定期的なポイントでした。便利な方法は多数ありますが、Perlのユーザーと実装者の両方にとって問題があり、混乱を招くことが判明しています。問題への最善の対処方法については、多くの提案がありました。スマートマッチがほぼ確実に変化するか、将来的に廃止されることは明らかです。現在の動作に依存することはお勧めしません。

パーサーが~~、指定された、またはいつ表示されるかを警告するようになりました。




Perl v5.10を必要とすることを回避できる場合は、次の例のいずれかを使用できます。

  • smart match ~~ 演算子。

    if( $element ~~ @list ){ ... }
    if( $element ~~ [ 1, 2, 3 ] ){ ... }
    
  • given/when 構文も使用できます。内部でスマートマッチ機能を使用します。

    given( $element ){
       when( @list ){ ... }
    }
    
  • forループを「topicalizer」として使用することもできます(つまり、$_を設定します)。

    for( @elements ){
       when( @list ){ ... }
    }
    

Perl 5.12で出てくるものの1つは、whenの修正後バージョンを使用する機能です。 ifunlessのようになります。

given( $element ){
  ... when @list;
}

古いバージョンのPerlで実行できるようにする必要がある場合、まだいくつかのオプションがあります。

  • List :: Util :: first を使用しても大丈夫だと思うかもしれませんが、問題を引き起こすエッジ条件がいくつかあります。

    この例では、0に対して正常に一致させたいことが明らかです。残念ながら、このコードは毎回failureを出力します。

    use List::Util qw'first';
    my $element = 0;
    if( first { $element eq $_ } 0..9 ){
      print "success\n";
    } else {
      print "failure\n";
    }
    

    Defined-nessの first の戻り値を確認できますが、実際にundefとの一致が必要な場合は失敗します。

  • ただし、安全に grep を使用できます。

    if( grep { $element eq $_ } 0..9 ){ ... }
    

    grepはスカラーコンテキストで呼び出されるため、これは安全です。配列は、スカラーコンテキストで呼び出されたときに要素の数を返します。したがって、undefに一致させようとしても、これは機能し続けます。

  • 囲むforループを使用できます。 lastを呼び出して、一致が成功した場合にループを終了することを確認してください。そうしないと、コードを複数回実行することになります。

    for( @array ){
      if( $element eq $_ ){
        ...
        last;
      }
    }
    
  • forステートメントの条件内にifループを配置できます...

    if(
      do{
        my $match = 0;
        for( @list ){
          if( $element eq $_ ){
            $match = 1;
            last;
          }
        }
        $match; # the return value of the do block
      }
    ){
      ...
    }
    
  • ...しかし、for文の前にifループを置く方がより明確かもしれません。

    my $match = 0;
    for( @list ){
      if( $_ eq $element ){
        $match = 1;
        last;
      }
    }
    
    if( $match ){ ... }
    
  • 文字列のみを照合する場合は、ハッシュも使用できます。 @listが大きいandの場合、これによりプログラムを高速化できます。%hashと数回照合します。特に、@arrayが変更されない場合は、%hashを一度ロードするだけでよいためです。

    my %hash = map { $_, 1 } @array;
    if( $hash{ $element } ){ ... }
    
  • 独自のサブルーチンを作成することもできます。これは、 prototypes を使用すると便利なケースの1つです。

    sub in(&@){
      local $_;
      my $code = shift;
      for( @_ ){ # sets $_
        if( $code->() ){
          return 1;
        }
      }
      return 0;
    }
    
    if( in { $element eq $_ } @list ){ ... }
    
109
Brad Gilbert
if( $element ~~ @list ){
   do_task
}

~~は「スマートマッチ演算子」であり、リストメンバーシップの検出以上のものを実行します。

15
jrockway

リスト:: Util :: first

$foo = first { ($_ && $_ eq "value" } @list;    # first defined value in @list

または手巻きタイプの場合:

my $is_in_list = 0;
foreach my $elem (@list) {
    if ($elem && $elem eq $value_to_find) {
        $is_in_list = 1;
        last;
    }
}
if ($is_in_list) {
   ...

わずかに異なるバージョンは、非常に長いリストで多少速くなる場合があります。

my $is_in_list = 0;
for (my $i = 0; i < scalar(@list); ++$i) {
    if ($list[i] && $list[i] eq $value_to_find) {
        $is_in_list = 1;
        last;
    }
}
if ($is_in_list) {
   ...
9
DVK

これを何度も行う予定がある場合は、ルックアップ時間とスペースをトレードオフできます。

#!/usr/bin/Perl

use strict; use warnings;

my @array = qw( one ten twenty one );
my %lookup = map { $_ => undef } @array;

for my $element ( qw( one two three ) ) {
    if ( exists $lookup{ $element }) {
        print "$element\n";
    }
}

要素が@arrayに現れる回数は重要ではなく、@arrayの内容は単純なスカラーであると仮定します。

8
Sinan Ünür

List :: MoreUtils

Perl> = 5.10では、他の多くの人がすでに言っているように、スマートマッチ演算子は確かに最も簡単な方法です。

古いバージョンのPerlでは、代わりに List :: MoreUtils :: any をお勧めします。

List::MoreUtilsはコアモジュールではありません(あるべきだと言う人もいます)が、非常に人気があり、主要なPerlディストリビューションに含まれています。

次の利点があります。

  • (Pythonのinのように)true/falseを返し、List::Util::firstのように要素の値を返しません(上記のようにテストが難しくなります)。
  • grepとは異なり、テストに合格する最初の要素で停止します(Perlのスマートマッチ演算子shortcircuits)。
  • perlバージョンで動作します(少なくとも5.00503以上)。

undefを含む、任意の検索(スカラー)値で機能する例を次に示します。

use List::MoreUtils qw(any);

my $value = 'test'; # or any other scalar
my @array = (1, 2, undef, 'test', 5, 6);

no warnings 'uninitialized';

if ( any { $_ eq $value } @array ) {
    print "$value present\n"
}

追伸.

(量産コードでは、no warnings 'uninitialized'の範囲を狭める方が良いでしょう)。

7
emazep

TIMTOWTDI

sub is (&@) {
  my $test = shift;
  $test->() and return 1 for @_;
  0
}

sub in (@) {@_}

if( is {$_ eq "a"} in qw(d c b a) ) {
  print "Welcome in Perl!\n";
}

grep はここで役立ちます

if (grep { $_ eq $element } @list) {
    ....
}
5
mob

恐らく Perl6::Junctionが最も明確な方法です。 XS依存関係、混乱、新しいPerlバージョンは必要ありません。

use Perl6::Junction qw/ any /;

if (any(@grant) eq 'su') {
    ...
}
3
drjumper

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

要約すると、CPANモジュールをインストールできる場合、最適なソリューションは次のとおりです。

if any(@ingredients) eq 'flour';

または

if @ingredients->contains('flour');

ただし、より一般的なイディオムは次のとおりです。

if @any { $_ eq 'flour' } @ingredients

私はあまりわかりません。

ただし、first()関数は使用しないでください!コードの意図をまったく表現していません。 「スマートマッチ」演算子は使用しないでください。壊れています。また、grep()もハッシュを使用したソリューションも使用しないでください。リスト全体を反復処理します。 any()は、値が見つかるとすぐに停止します。

詳細については、ブログ投稿をご覧ください。

PS:将来同じ質問をする人のために答えています。

2
mascip

Autoload ハッキングを行うと、Perlで同様の十分な構文を実現できます。

自動ロードを処理する小さなパッケージを作成します。

package Autoloader;
use strict;
use warnings;

our $AUTOLOAD;

sub AUTOLOAD {
    my $self     = shift;
    my ($method) = (split(/::/, $AUTOLOAD))[-1];

    die "Object does not contain method '$method'" if not ref $self->{$method} eq 'CODE';

    goto &{$self->{$method}};
}

1;

次に、他のパッケージまたはメインスクリプトには、メソッドの呼び出しが試行されたときにAutoloadによって処理される祝福されたオブジェクトを返すサブルーチンが含まれます。

sub element {
    my $elem = shift;

    my $sub = {
        in => sub {
            return if not $_[0];

            # you could also implement this as any of the other suggested grep/first/any solutions already posted.
            my %hash; @hash{@_} = ();
            return (exists $hash{$elem}) ? 1 : ();
        }
    };

    bless($sub, 'Autoloader');
}

これにより、次のような使用法が得られます。

doTask if element('something')->in(@array);

クロージャとその引数を再編成する場合、構文を他の方法に切り替えて、このように見えるようにすることができます。これは、オートボックススタイルに少し似ています。

doTask if search(@array)->contains('something');

それを行う関数:

sub search {
    my @arr = @_;

    my $sub = {
        contains => sub {
            my $elem = shift or return;
            my %hash; @hash{@arr} = ();
            return (exists $hash{$elem}) ? 1 : ();
        }
    };

    bless($sub, 'Autoloader');
}
2
delias