开发者

Is there a perl "not in" operator?

Suppose I have an array of numbers and I want to ensure that all of these fall within one of the set (x,y,z), I'm currently checking that the following evaluates to 0 :

scalar ( grep { $_ ne x &&开发者_如何学Camp; $_ ne y && $_ ne z } @arr )

Was just wondering if it'll not be easier if we had "IN" and "NOT IN" sql-like operators in perl too..

scalar ( grep { $_ NOT IN (x,y,z) } @arr )

Or is there one already ??

Thanks, Trinity


The libraries List::Util or List::MoreUtils are very useful here for testing list membership where you don't care about the values itself, but simply existence. These are more efficient than grep, because they stop looping through the list as soon as a match is found, which can really speed things up with long lists. Moreover these modules are written in C/XS, which is faster than any pure-perl implementation.

use List::MoreUtils 'any';

my @list = qw(foo bar baz);

my $exists = any { $_ eq 'foo' } @list;
print 'foo ', ($exists ? 'is' : 'is not'), " a member of the list\n";

$exists = any { $_ eq 'blah' } @list;
print 'blah ', ($exists ? 'is' : 'is not'), " a member of the list\n";

(If you are restricted to only using modules that come with core Perl, you can use first in List::Util -- it first shipped with perl in 5.7.3.)


A typical way to solve this is to use a hash:

my %set = map {$_ => 1} qw( x y z ); # add x, y and z to the hash as keys
                                     # each with a value of 1

my @not_in_set = grep {not $set{$_}} @arr;


If you're using Perl 5.10 or above (or willing to use an experimental feature in perl 5.18 and up), the Smart Match operator will do exactly what you're looking for.

# if Perl 5.18 or higher; otherwise not needed
no warnings 'experimental::smartmatch';

my @filter = qw(X Y Z);
my $not_in_filter = scalar grep { ! ($_ ~~ @filter) } @array;

If Filter and/or @array are large, it may be slow, however, due to it being O(N^2). In that case, you can still use smart matching, and just change your filter:

my %filter = map { $_ => 1 } qw(X Y Z);
my $not_in_filter = scalar grep { ! ($_ ~~ %filter) } @array;

See Smart Matching in Detail in perldoc perlsyn for more info.

Also, if you need to support versions of perl between 5.10 and 5.18, consider using the cpan module experimental. This does version checks and includes the 'no warnings' if it finds a Perl version that requires it.

use experimental 'smartmatch';

See: https://search.cpan.org/~leont/experimental-0.016/lib/experimental.pm


If there are less than a few million different things in the array, you could also use the textbook method of set difference using a hash:

my %seen;
@seen{ @arr } = (); # Create a key for every distinct value in @arr
delete @seen{ qw(x y z) }; # And remove the ones for x, y, and z

if (keys %seen) {
    # There was something in @arr that's not x, y, or z
} else {
    # There wasn't
}


my $target = 'bar';
my @look_in = ('foo','baz','bar','etc');
if( $target ~~ @look_in) {
    print "target is in array ";
}

~~ is in array

my $target = 'bar';
my @look_in = ('foo','baz','bar','etc');
if( not $target ~~ @look_in) {
    print "target is not in array";
}

this is called smartmatch, some people recommend not to use them, but they work quite nice with list of strings.


use List::Member;
my $target = 'bar';
my @look_in = ('foo','baz','bar','etc');

if( member($target, @look_in) + 1) {
 print "It's a member of the array\n";
}

That can do the trick

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜