Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Hash option/menu loop wierd or usefull?

by mulander (Monk)
on Dec 07, 2005 at 17:16 UTC ( [id://514908]=perlmeditation: print w/replies, xml ) Need Help??

Hellow fellow Monks,
I recently came up with a really weird way to use perl's
hashes. Maybe someone tried it before and found it
usefull or maybe I just came up with the most usless
thing ever ;)
Here is the example code:
#!/usr/bin/perl use warnings; use strict; my %options = ( one => \&one, two => \&two, three => \&three, ); my $ret; # return code from the sub while(chomp(my $opt = <STDIN>)){ if(exists $options{$opt}) { $ret = &{$options{$opt}}; last if $ret == 0; } else { default(); } } sub one { print "one\n"; return 1; } sub two { print "two\n"; return 1; } sub three { print "three and break the loop\n"; return 0; } sub default { print "Default\n"; }
I thought of it as a method to avoid a really long list of
if-elsif-elsif-elsif-elsif-else chain, and as Perlmonks
is full of people that understand perl much better than
I do so I decided to contribute this idea, and would
really like to know your honest opinions on such a weird
hash usage.

Here are the questions:
a) Is it worth using?
b) Is this will be taken as a 'bad style' example?
c) Is if-elsif-elsif-elsif-else chain more efficient than this?

Thank you all for taking the time to read this node.
Regards,
Mulander

Replies are listed 'Best First'.
Re: Hash option/menu loop wierd or usefull?
by friedo (Prior) on Dec 07, 2005 at 17:42 UTC
    What you've got there is called a Dispatch Table and it is a useful (and common) technique. I'm sure you can pull up some interesting nodes via Super Search if you look for that term.

    I would change this line in your code:

    $ret = &{$options{$opt}};

    To this:

    $ret = $options{$opt}->();

    a) Is it worth using?

    Definitely. It's a great way to handle a large number of options, IMHO.

    b) Is this will be taken as a 'bad style' example?

    It's a standard technique, so as long as you've got some comments explaining that it's a dispatch table it's fine.

    c) Is if-elsif-elsif-elsif-else chain more efficient than this?

    It might be, depending on how many options there are. Hash lookups are roughly constant-time operations, whereas a chain of if-elsif-else blocks would increase in time for every new condition added. Further, dividing separate tasks up into small subs is far better from a maintenance standpoint than a gigantic conditional structure.

    Update: Expanded answer a bit.

      Thanks friedo, I was lacking the name of such hash usage, maybe that's why I didn't manage to find nodes about it. Thanks again.

      There's another problem with if () elsif () elsif () ... else chains that bears mentioning.

      It's an artifact of the way Perl source code is compiled down into the form used by the bytecode interpreter running underneath. The problem is that if any expression in any of the elsif conditions throws a warning (for instance, performing a comparison with a variable set to undef will emit an "Uninitialized variable" warning), then perl will report that the problem occurred on the line containing the if, not the actual line in question.

      This is, as you can imagine, a royal pain when debugging if you're not aware of the quirk. In fact it causes a steady trickle of bug reports to be filed. The perl5-porters are currently evaluating how much extra memory it would take to save the additional line number information (that is currently being discarded) into the resulting bytecode in order to report the correct line number. So it may be fixed one day, but until then, dispatch tables are a good way of avoiding this gotcha.

      • another intruder with the mooring in the heart of the Perl

Re: Hash option/menu loop wierd or usefull?
by Aristotle (Chancellor) on Dec 26, 2005 at 09:59 UTC

    It has already been mentioned that this is a known and useful technique. A way to improve it further in cases when you don’t actually need these functions elsewhere in the code is to put inline them in the hash assignment as anonymous functions:

    my %options = ( one => sub { print "one\n"; return 1; }, two => sub { print "two\n"; return 1; }, three => sub { print "three and break the loop\n"; return 0; }, '' => sub { print "Default\n"; }, );

    (I put the default into the hash under the empty string key; the choice of key is arbitrary, it’s just usually a good idea to keep everything together.)

    Welcome to the world of first-class functions. :-)

    Note also that references are always considered true, so instead of something like

    if( exists $dispatch{ $action } ) { $dispatch{ $action }->(); } else { $default->(); }

    you can just say

    &{ $dispatch{ $action } || $default }();

    This lets you simplify your code:

    while ( chomp( my $opt = <STDIN> ) ) { &{ $options{ $opt } || $options{ '' } }() or last; }

    Also, I notice in your code that you declared $ret outside the loop body. Unless you really need to use it outside the loop (which in your case, with the return code only being 0 or 1 and then only serving for control flow, you obviously don’t), it is good habit to declare it inside the loop instead. Keep variables scoped as tightly as at all possible.

    Makeshifts last the longest.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://514908]
Approved by friedo
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (7)
As of 2024-04-23 15:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found