What is the idiomatic way in Perl to determine if a string variable matches a string in a list?

Part of the specification reads: β€œSome names are special, such as Hughie, Dewey, Louis and Donald. Other names can be added throughout the project’s life cycle at any time. Whenever you enter one of these names, play quackery. WAV ".

I could write ...

while (<>) { if ($_ =~ /Hughie|Dewey|Louis/) { quack() ; } elsif ($_ =~ /Donald/ { quack() ; you_re_fired_apprentice() ; # Easter egg don't tell QA } } 

... but although not recommended, it looks like WTF-y: where is the binary search? What if there were a sudden huge increase in the number of duck names? It will not scale at all!

I can create empty files using these names in a temporary directory and then use the "file exists" API, but that seems cool, and I had to be sure that they were deleted at the end.

Of course, is there a better way?

+7
source share
6 answers

You could write this, but you should write this:

 my %ducks = map {$_ => 1} qw(Hughie Dewey Louis); while (<>) { if ($ducks{$_}) { quack() ; } elsif ($_ eq 'Donald') { quack() ; you_re_fired_apprentice() ; # Easter egg don't tell QA } } 

Creating a hash takes a little time, but no more than O(n) . A search with an O(1) hash, though, so it’s much more efficient than a sequential search (via grep or interleaved regular expression), assuming that you will check for more than one or two elements.

By the way, the regular expression that you have will match the words anywhere in the search string. You need to add start and end anchors if you want an exact match.

+7
source

Alternatively you can use smart matching

 my @ducks = qw(Hughie Dewey Louis); my $name = 'Dewey'; say 'smart match' if $name ~~ @ducks; 

This is what is used by switch statements , so you can write

 given ($name) { when (@ducks) { quack(); } when ('Donald') { quack(); you_re_fired_apprentice(); # Easter egg don't tell QA } } 
+7
source

As already mentioned, hashes are the way to go. This is similar to what OOP looked in front of OOP.

 use strict; use warnings; my %duck_action = ( Donald => sub {quack(); you_re_fired_apprentice()}, Hughie => sub {quack()}, Dewie => sub {quack()}, Louis => sub {quack()}, ); for my $duck (qw( Hughie Dewie Donald Louis Porkie )) { print "$duck: "; my $action = $duck_action{$duck} || &no_such_duck; $action->(); } sub quack { print "Quack!\n"; } sub you_re_fired_apprentice { print "You're fired!\n"; } sub no_such_duck { print "No such duck!\n"; } 
+4
source

You can use Perl Hash . See Also. How can I introduce sets in Perl? and Representation of sets in Perl .

Using hashes to implement a set is not entirely beautiful, but it should be fast.

+3
source

To find a string in a list, you can also use any in List :: MoreUtils

 use List::MoreUtils qw(any); my @ducks = qw(Hughie Dewey Louis); my $name = 'Dewey'; say 'any' if any {$name eq $_} @ducks; 
+2
source

If you're tied to using an array, not a hash, you can use the perl grep function to find the array for the string.

 @specialnames = qw(Hughie Dewey Louis); while (my $value = <>) { if (grep {$value eq $_}, @specialnames) { quack() ; } elsif ($_ =~ /Donald/ { quack() ; you_re_fired_apprentice() ; # Easter egg don't tell QA } } 

This is much worse than a hash, and can even get worse even worse than copying an array to a hash and then doing a hash lookup.

+1
source

All Articles