Re: Sparing multiple 'or's
by Tux (Canon) on Jun 04, 2018 at 15:25 UTC
|
Personally, I'd use a hash:
my %expr = (
EX_CASE1 => { map { $_ => 1 } qw( ABA SCO ACC PHC GHF ) },
EX_CASE2 => {}, # …
);
:
:
if (exists $expr{EX_CASE1}{$a}) {
...
}
:
if (exists $expr{EX_CASE2}{$b}) {
...
}
but I'd choose more expressive names than EX_CASE1 etc :)
You can also use List::Util's any, but that might read fine, but is very hard to optimize, so it will probably be slower the your current code
use List::Util qw( any );
if (any { $a eq $_ } qw( ABA SCO ACC PHC GHF )) {
....
}
Enjoy, Have FUN! H.Merijn
| [reply] [d/l] [select] |
|
|
++ For the hash method, although your nested structure makes it look more complex than the other solutions. It can be made to look much simpler using a single level, and the fact that there won't be autovivification in boolean context:
# equivalent to %is_valid = (ABA => 1, SCO => 1, ACC => 1...);
my %is_valid = map { $_ => 1 } qw( ABA SCO ACC PHC GHF );
if ($is_valid{$variable}) # Easy to read
{
...
}
| [reply] [d/l] |
|
|
I coded for expansion. With the OP's case, I do expect their code to be littered with statements like that. What would you choose? On hash, like I did, with explicit names/keys indicating the purpose of the hash entry (easily expandable) or numerous single-level hashes, all maintained differently.
If it is just for this single statement, it is a fun-to-read thread for all approaches, but none is an actual improvement over the original or'd eqs. I would not change a thing. If this kind of expression will appear all over the code, I'd choose the simplest to maintain method. YMMV.
Enjoy, Have FUN! H.Merijn
| [reply] [d/l] |
|
|
Re: Sparing multiple 'or's
by Eily (Monsignor) on Jun 04, 2018 at 15:24 UTC
|
You can either use grep like this: if (grep {$variable eq $_} qw( ABA SCO ACC PHC GHF )) (where qw builds a list of words).
Or use List::Util's any (with its more intuitive name, and slightly faster since it will stop searching as soon as a match is found):
use List::Util qw( any );
if (any {$variable eq $_} qw( ABA SCO ACC PHC GHF ))
$a is a special variable, than can be used by sort or some functions of List::Util, that's why I used $variable instead.
And also note that I used eq, which compares two strings, while == compares numbers. Perl will actually complain about this if you have warnings enabled (which you really should, as it lets perl tell you where it thinks you have done a mistake, and it is often right). | [reply] [d/l] [select] |
Re: Sparing multiple 'or's
by dave_the_m (Monsignor) on Jun 04, 2018 at 15:26 UTC
|
I'm presuming you intended string rather than numeric comparisons (so $a eq 'ABA'). In which case this does it:
if ($a =~ /^ ( ABA | SCO | ACC | PHC | GHF ) $/x ) { ... }
Dave. | [reply] [d/l] [select] |
|
|
if ( $a =~ /^ (?: ABA | SCO | ACC | PHC | GHF ) $/x ) { ... }
Note that this is more for capturing intent than performance. When I see a capture group (...), I assume we will be using whatever was captured. In this case, since we are capturing the entire string, $a == $1.
Probably not worth the nitpick :)
Best,
Jim
πάντων χρημάτων μέτρον έστιν άνθρωπος.
| [reply] [d/l] [select] |
Re: Sparing multiple 'or's
by hippo (Archbishop) on Jun 04, 2018 at 15:25 UTC
|
TIMTOWTDI, but here are two methods (grep and regex) to get you started:
#!/usr/bin/env perl
use strict;
use warnings;
my @cases = qw/ABA SCO ACC PHC GHF/;
my $re = join '|', @cases;
for my $text ('SCO', 'Microsoft') {
print "$text found (grep)\n" if grep {$text eq $_} @cases;
print "$text found (regex)\n" if $text =~ /^$re$/;
}
PS. It would be better not to use $a as a variable name since that is special with respect to sorting. | [reply] [d/l] [select] |
|
|
c:\@Work\Perl\monks>perl -wMstrict -le
"my @cases = qw/ABA SCO ACC PHC GHF/;
my $re = join '|', @cases;
;;
for my $text ('SCO', 'ENDOSCOPE', 'Microsoft') {
print qq{'$text' found (grep)} if grep { $text eq $_ } @cases;
print qq{'$text' found (regex)} if $text =~ /^$re$/;
}
"
'SCO' found (grep)
'SCO' found (regex)
'ENDOSCOPE' found (regex)
See haukex's Building Regex Alternations Dynamically.
Give a man a fish: <%-{-{-{-<
| [reply] [d/l] [select] |
|
|
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
my @cases = qw/ABA SCO ACC PHC GHF/;
my $re = join '|', @cases;
for my $text ('SCO', 'ENDOSCOPE', 'Microsoft') {
print "$text found (grep)\n" if grep {$text eq $_} @cases;
print "$text found (regex)\n" if $text =~ /^($re)$/;
}
| [reply] [d/l] |
Re: Sparing multiple 'or's
by LanX (Saint) on Jun 04, 2018 at 15:26 UTC
|
grep in scalar context
grep { $a eq $_ } 'ABA', 'SCO', 'ACC', 'PHC' , 'GHF'
gives you the number of successful tests.
I.e. it's true in boolean context as long as it matches at least once.
HTH! :)
| [reply] [d/l] |
|
|
I would prefer to use the function 'any' from List::MoreUtils rather than 'grep' because the name seems to make the intention clearer. It may even be slightly faster because it can quit at the first match.
| [reply] |
|
|
| [reply] [d/l] [select] |
|
|
|
|
|
|
>corelist List::MoreUtils
Data for 2013-01-20
List::MoreUtils was not in CORE (or so I think)
| [reply] [d/l] |
Re: Sparing multiple 'or's
by bliako (Abbot) on Jun 04, 2018 at 22:05 UTC
|
Denis, you have not told us whether your flags are mutually exclusive ($a can contain ABA - only) or they can be turned on at the same time ($a can contain ABA and SCO).
In the olden days, in the caves, they used *flags*. Each flag was denoted by a digit in a binary word. The binary word making up the "options". Flags as binary words had the added benefit of having a bunch of them turned on at the same time. The negative side is that they are all binary/integers and made little sense to the user without consulting the manual - though most times they were internal and nobody cared about their values, e.g. C's open()'s O_CREAT and O_APPEND flags.
Here is a refresher (translated from C):
#!/usr/bin/env perl
use strict;
use warnings;
use Getopt::Long;
# our constants
use constant ABA => 0b00001; # 0b... for binary number
use constant SCO => 0b00010;
use constant ACC => 0b00100;
my $a = 0b0;
Getopt::Long::GetOptions(
'ABA' => sub { $a |= ABA },
'SCO' => sub { $a |= SCO },
'ACC' => sub { $a |= ACC },
) or die "error command line";
if( $a & SCO ){ print "it has SCO!\n"; }
if( $a & ABA ){ print "it has ABA!\n"; }
if( $a & ACC ){ print "it has ACC!\n"; }
This is quite fast but I am sure there will be aesthetic objections including mine (which balances with my sentimental counter-objections).
In the Present, we do away with all these and instead use string constants and string comparisons which are more expensive.
I will not push any further the point of "cost" and "speed", but I will highlight the point of "multiple flags coexisting in a variable". Which is very useful as it reflects real-life situations. I am sure whizkids can make flags stored in strings turned on at the same time by devising a way to encode them in a string which can be robust yet ... quite unreadable, like the binary flags.
Against the binary flags is the MAXINT limitation which let's say is 2^64, it will limit the max number of flags to check on to 64 :(
PS. If this forum were another, I am sure someone would have suggested a class for handling this kind of situations.
PS2. Flags and Semaphores! | [reply] [d/l] |
Re: Sparing multiple 'or's
by haukex (Archbishop) on Jun 04, 2018 at 19:54 UTC
|
| [reply] |
Re: Sparing multiple 'or's
by davido (Cardinal) on Jun 05, 2018 at 15:07 UTC
|
my @alternatives = qw(ABA SCO ACC PHC GHF);
my $target = 'ABA';
if (grep {$target eq $_} @alternatives) {
print "$target is in (@alternatives)\n";
}
else {
print "$target is not in (@alternatives)\n";
}
The meat is this: if (grep {$target eq $_} @alternatives) ....
Here's how it works. First, grep will iterate over the elements in @alternatives, and compare each element to your $target value. In scalar context (provided by the if(CONDITION) construct), grep returns the number of matches. In Perl, any numeric value that is not zero is true in a Boolean sense. So getting a match on one of the elements will make the condition true.
You can set up an "all" relationship (instead of "any") like this:
if (@alternatives == grep {$target eq $_} @alternatives) {...
Here we're asserting that every element in @alternatives must match $target.
| [reply] [d/l] [select] |
Re: Sparing multiple 'or's
by Denis (Initiate) on Jun 04, 2018 at 19:35 UTC
|
Gentlemen,
thank you all for the incredible support you've given!
I never thought anyone would bother answering! | [reply] |
Re: Sparing multiple 'or's
by tobyink (Canon) on Jun 06, 2018 at 12:14 UTC
|
My module match::simple provides a match function which can be used for this purpose. It's pretty fast, but even faster if you install match::simple::XS.
use match::simple qw(match);
if (match $a => [qw{ ABA SCO ACC PHC GHF }]) {
...;
}
| [reply] [d/l] [select] |
Re: Sparing multiple 'or's
by clueless newbie (Curate) on Jun 04, 2018 at 21:00 UTC
|
Smart match?
if ($a ~~ qw(ABA SCO ACC PHC GHF)) { ... }
| [reply] [d/l] |
|
|
| [reply] |
Re: Sparing multiple 'or's
by karlgoethebier (Abbot) on Jun 08, 2018 at 07:35 UTC
|
#!/usr/bin/env perl
use strict;
use warnings;
use English;
use feature qw(say);
sub any (&@);
my $item = 'ABA';
my @items = qw(ABA SCO ACC PHC GHF);
if ( any { $ARG eq $item } @items ) {
say q(Cool!);
}
else {
say q(Crap!);
}
sub any (&@) {
my $f = shift;
foreach (@_) {
return 1 if $f->();
}
return 0;
}
__END__
See also
Best regards, Karl
«The Crux of the Biscuit is the Apostrophe»
perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help
| [reply] [d/l] [select] |
| A reply falls below the community's threshold of quality. You may see it by logging in. |