I'm looking for presence of an element in a list.
In Python there is an in
keyword and I would do something like:
if element in list:
doTask
Is there something equivalent in Perl without having to manually iterate through the entire list?
I'm looking for presence of an element in a list.
In Python there is an in
keyword and I would do something like:
if element in list:
doTask
Is there something equivalent in Perl without having to manually iterate through the entire list?
if( $element ~~ @list ){
do_task
}
~~
is the "smart match operator", and does more than just list membership detection.
$foo = first { ($_ && $_ eq "value" } @list; # first defined value in @list
Or for hand-rolling types:
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) {
...
A slightly different version MIGHT be somewhat faster on very long lists:
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) {
...
If you plan to do this many times, you can trade-off space for lookup time:
#!/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";
}
}
assuming that the number of times the element appears in @array
is not important and the contents of @array
are simple scalars.
The smart match ~~
operator.
if( $element ~~ @list ){ ... }
if( $element ~~ [ 1, 2, 3 ] ){ ... }
You could also use the given
/when
construct. Which uses the smart match functionality internally.
given( $element ){
when( @list ){ ... }
}
You can also use a for
loop as a "topicalizer" ( meaning it sets $_
).
for( @elements ){
when( @list ){ ... }
}
One thing that will come out in Perl 5.12 is the ability to use the post-fix version of when
. Which makes it even more like if
and unless
.
given( $element ){
... when @list;
}
You might think you can get away with using List::Util::first, but there are some edge conditions that make it problematic.
In this example it is fairly obvious that we want to successfully match against 0
. Unfortunately this code will print failure
every time.
use List::Util qw'first';
my $element = 0;
if( first { $element eq $_ } 0..9 ){
print "success\n";
} else {
print "failure\n";
}
You could check the return value of first
for defined-ness, but that will fail if we actually want a match against undef
to succeed.
You can safely use grep
however.
if( grep { $element eq $_ } 0..9 ){ ... }
This is safe because grep
gets called in a scalar context. Arrays return the number of elements when called in scalar context. So this will continue to work even if we try to match against undef
.
You could use an enclosing for
loop. Just make sure you call last
, to exit out of the loop on a successful match. Otherwise you might end up running your code more than once.
for( @array ){
if( $element eq $_ ){
...
last;
}
}
You could put the for
loop inside the condition of the if
statement ...
if(
do{
my $match = 0;
for( @list ){
if( $element eq $_ ){
$match = 1;
last;
}
}
$match; # the return value of the do block
){
...
}
... but it might be more clear to put the for
loop before the if
statement.
my $match = 0;
for( @list ){
if( $_ eq $element ){
$match = 1;
last;
}
}
if( $match ){ ... }
If you're only matching against strings, you could also use a hash. This can speed up your program if @list
is large and, you are going to match against %hash
several times. Especially if @array
doesn't change, because then you only have to load up %hash
once.
my %hash = map { $_, 1 } @array;
if( $hash{ $element } ){ ... }
You could also make your own subroutine. This is one of the cases where it is useful to use prototypes.
sub in(&@){
local $_;
my $code = shift;
for( @_ ){ # sets $_
if( $code->() ){
return 1;
}
}
return 0;
}
if( in { $element eq $_ } @list ){ ... }
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";
}