リスト内の要素の存在を探しています。
Pythonにはin
キーワードがあり、次のようなことをします。
if element in list:
doTask
リスト全体を手動で繰り返すことなく、Perlに同等のものがありますか?
V5.10.0で追加され、v5.10.1で大幅に改訂されたスマートマッチは、不満の定期的なポイントでした。便利な方法は多数ありますが、Perlのユーザーと実装者の両方にとって問題があり、混乱を招くことが判明しています。問題への最善の対処方法については、多くの提案がありました。スマートマッチがほぼ確実に変化するか、将来的に廃止されることは明らかです。現在の動作に依存することはお勧めしません。
パーサーが~~、指定された、またはいつ表示されるかを警告するようになりました。
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
の修正後バージョンを使用する機能です。 if
とunless
のようになります。
given( $element ){
... when @list;
}
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 ){ ... }
if( $element ~~ @list ){
do_task
}
~~
は「スマートマッチ演算子」であり、リストメンバーシップの検出以上のものを実行します。
$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) {
...
これを何度も行う予定がある場合は、ルックアップ時間とスペースをトレードオフできます。
#!/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
の内容は単純なスカラーであると仮定します。
Perl> = 5.10では、他の多くの人がすでに言っているように、スマートマッチ演算子は確かに最も簡単な方法です。
古いバージョンのPerlでは、代わりに List :: MoreUtils :: any をお勧めします。
List::MoreUtils
はコアモジュールではありません(あるべきだと言う人もいます)が、非常に人気があり、主要なPerlディストリビューションに含まれています。
次の利点があります。
in
のように)true/falseを返し、List::Util::first
のように要素の値を返しません(上記のようにテストが難しくなります)。grep
とは異なり、テストに合格する最初の要素で停止します(Perlのスマートマッチ演算子shortcircuits)。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'
の範囲を狭める方が良いでしょう)。
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) {
....
}
恐らく Perl6::Junction
が最も明確な方法です。 XS依存関係、混乱、新しいPerlバージョンは必要ありません。
use Perl6::Junction qw/ any /;
if (any(@grant) eq 'su') {
...
}
このブログ投稿 は、この質問に対する最良の回答について説明しています。
要約すると、CPANモジュールをインストールできる場合、最適なソリューションは次のとおりです。
if any(@ingredients) eq 'flour';
または
if @ingredients->contains('flour');
ただし、より一般的なイディオムは次のとおりです。
if @any { $_ eq 'flour' } @ingredients
私はあまりわかりません。
ただし、first()関数は使用しないでください!コードの意図をまったく表現していません。 「スマートマッチ」演算子は使用しないでください。壊れています。また、grep()もハッシュを使用したソリューションも使用しないでください。リスト全体を反復処理します。 any()は、値が見つかるとすぐに停止します。
詳細については、ブログ投稿をご覧ください。
PS:将来同じ質問をする人のために答えています。
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');
}