Reputation: 632
Sometimes in Perl, I write a for
/ foreach
loop that iterates over values to check a value against a list. After the first hit, the loop can exit, as we've satisfied my test condition. For example, this simple code:
my @animals = qw/cat dog horse/;
foreach my $animal (@animals)
{
if ($input eq $animal)
{
print "Ah, yes, an $input is an animal!\n";
last;
}
}
# <-----
Is there an elegant way - an overloaded keyword, maybe - to handle "for loop reached last element"? Something to put at the arrow above?
I can think of ways to do this, like creating / setting an additional $found
variable and testing it at the end.... But I was hoping Perl might have something else built in, like:
foreach my $animal (@animals)
{
if ($input eq $animal)
{
print "Ah, yes, an $input is an animal!\n";
last;
}
} finally {
print "Sorry, I'm not sure if $input is an animal or not\n";
}
which would make this test more intuitive.
Upvotes: 4
Views: 1999
Reputation: 66883
It's natural with any
from List::Util library
use List::Util qw(any);
if ( any { $input eq $_ } @animals ) {
say "yes $input is among animals"
}
else {
say "No $input in animals"
}
Upvotes: 2
Reputation: 385647
It's not the best solution here, but sometimes it helps to iterates over the indexes.
for my $i (0..$#animals) {
my $animal = $animals[$i];
...
}
Then, you can check if the index is 0
(first pass) or $#animals
(last pass).
Upvotes: 1
Reputation: 97
First is going to be the least overhead; eval avoids having to nest it all in if-blocks; newline because you probably don't really care what line it wasn't an animal on.
eval
{
my $found = first { check for an animal } @animalz
or die "Sorry, no animal found.\n";
# process an animal
1
}
// do
{
# deal with non-animals
};
Upvotes: 0
Reputation: 191
I'd keep it Old School and use a well-known C-idiom (for-loop split up in 1st statement and a while-loop).
#!/usr/bin/env perl
use strict;
use warnings;
my $input = 'lion';
my @animals = qw/cat dog horse/;
my $index = 0;
while ($index < scalar @animals) {
if ($animals[ $index++ ] eq $input) {
print "Ah, yes, an $input is an animal!\n";
last;
}
}
if ($index == scalar @animals) {
print "Sorry, I'm not sure if $input is an animal or not\n";
}
Thus, "Sorry, I'm not sure if lion is an animal or not" will come very naturally. Hope, this helps. Regards, M.
Upvotes: 1
Reputation: 9231
Just have the loop set a variable so you can check if it's been set and act on it later:
my $found;
foreach my $animal (@animals) {
if ($input eq $animal) {
$found = $animal;
last outer;
}
}
print defined $found ? "Ah, yes, an $input is an animal!\n" : "no animal found\n";
But for this particular problem, as @choroba says, just use the first
(or any
) function from List::Util. Or if you will be checking a lot of inputs, it's easier to check a hash.
my %animals = map { ($_ => 1) } qw/cat dog horse/;
print exists $animals{$input} ? "Ah, yes, an $input is an animal!\n" : "no animal found\n";
Upvotes: 2
Reputation: 3262
You can wrap your loop with a labeled block like so:
outer: {
foreach my $animal (@animals) {
if ($input eq $animal) {
print "Ah, yes, an $input is an animal!\n";
last outer;
}
}
print "no animal found\n";
}
Upvotes: 4