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
精彩评论