Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Creating dispatch table with variable in function name

by nysus (Priest)
on Nov 21, 2017 at 19:22 UTC ( #1203938=perlquestion: print w/replies, xml ) Need Help??
nysus has asked for the wisdom of the Perl Monks concerning the following question:

I'm creating a dispatch table. This works:

my %dispatch = map { $_, \&{$_} } qw(first last user_id email create_p +assword);

But this doesn't

my %dispatch = map { $_, \&{'_create_' . $_} } qw(first last user_id e +mail create_password);

How can I tack on a '_create_' before each function name?

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate";
$nysus = $PM . ' ' . $MCF;
Click here if you love Perl Monks

Replies are listed 'Best First'.
Re: Creating dispatch table with variable in function name
by Mr. Muskrat (Canon) on Nov 21, 2017 at 19:41 UTC
    It works for me.
    #!/bin/env perl use strict; use warnings; my %dispatch = map { $_, \&{'_create_' . $_} } qw(first last); sub _create_first { print "first!\n"; } sub _create_last { print "last!\n"; } $dispatch{first}->(); $dispatch{last}->(); __DATA__ first! last!

    Update: Do you have a subroutine called _create_create_password?

Re: Creating dispatch table with variable in function name
by stevieb (Abbot) on Nov 21, 2017 at 19:43 UTC

    It "works" fine for me:

    use warnings; use strict; use Data::Dumper; my %d = map { $_, \&{'create_' . $_} } qw(first); print Dumper \%d; $d{first}->(); sub create_first { print "first!\n"; }

    Output:

    $VAR1 = { 'first' => sub { "DUMMY" } }; first!

    ...so you need to describe what's not working as expected.

    Are you wanting to call the sub like this?:

    $dispatch{_create_first}->();

    If so, you need to map the '_create_' to both arguments to map. This example uses a second map() to modify the $_ variable on the way into the map that creates the actual dispatch table.

    use warnings; use strict; use Data::Dumper; my %d = map { $_, \&{$_} } map {'create_' . $_} qw(first); print Dumper \%d; $d{create_first}->(); sub create_first { print "first!\n"; }

    Output:

    $VAR1 = { 'create_first' => sub { "DUMMY" } }; first!

      Ugh, forgot the ->() bit to actually call the function. The sub { "DUMMY" } output when printing the variable was also throwing me off.

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

        Uf the sub { "DUMMY" } came from Data::Dumper, you can tell it to show the code by turning Deparse on:
        use Data::Dumper; $Data::Dumper::Deparse = 1; print Dumper(sub { $_[0] . $_[1] });

        ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: Creating dispatch table with variable in function name
by Laurent_R (Canon) on Nov 21, 2017 at 22:10 UTC
    Hmm, I am not sure it is a good idea to do this kind of things. It appears you're really tampering with the symbol tables. This is sort of similar to symbolic references (which are very much frown upon). Or, maybe, this kind of construct should be reserved only for special cases where you want to extend the language, but perhaps shouldn't be done for regular coding. You don't show enough for me to be able to suggest an alternative approach, however.

    I am not saying that this is bad, I am not quite sure, but I am just questioning or perhaps only wondering whether this is a good idea. I would be quite interested to know what other monks think about that.

      It's a simple hash with references to the subroutines so no, this doesn't tamper with the symbol tables.

        No, it's not a simple dispatch table with code refs in it, I certainly would not have any objections to that. What I am possibly worrying about is that the subroutine ref names are dynamically created.
      Dispatch tables (hashes with coderefs as values) are a relatively well-known "advanced" technique in Perl and pretty widely used and respected. Personally, I'm using it a lot these days to create programs with a "plugin"-type structure, but it's also used heavily in web frameworks or other contexts where you normally receive a string from an outside source (the UI, a config file, the other end of a network connection, etc.) telling you what to do, then look that string up to find out how to do it. It's simpler, more extensible, and more efficient to match the command to the implementation with a hash lookup than a long if-elsif chain.

      Regarding your concern about it seeming "sort of similar to symbolic references", the most common response I see to "use a variable as a variable name" questions is that you should instead put your data in a hash and look it up by name that way. This is the exact same thing. The hash just contains coderefs instead of static data values.

        Hi,

        I did not have time before to answer in a detailed fashion.

        This is an example (under the debugger) of the use of symbolic references:

        DB<1> ($blue, $red) = qw / b r/; DB<2> say ${$_} for qw /blue red/; b r
        What is happening here is that the second line constructs the variable name $blue from the string "blue" (and the same for red). It works and prints the values of the variables initialized in the first line. It works, yes, but we all know that this technique is very much frown upon, to the point that it is forbidden under the strict pragma, as shown with this one-liner:
        $ perl -Mstrict -E 'our ($blue, $red) = qw / b r/; say ${$_} for qw / +blue red/;' Can't use string ("blue") as a SCALAR ref while "strict refs" in use a +t -e line 1.

        Why does it work? Because it is constructing an entry (variable name) that can be found in the symbol table.

        You can even construct the name with concatenation, doing it this way:

        DB<3> say ${ "bl" . "ue"}; b

        Now the reason I was worried about the OP's code is that the way it constructs the subroutine name is quite similar, and with essentially the same intent: to create a name and look up the symbol table for the subroutine name.

        Again, I'm not saying this is bad, I'm just asking other monks what they think about it.

        Thanks for your answer. I am not objecting at all to using dispatch tables, I'm using dispatch tables quite regularly. What I am possibly worrying about is that the subroutine ref names are dynamically created by concatenating various peaces of the name and enclosing them in in &{...} construct.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1203938]
Approved by herveus
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (10)
As of 2018-06-20 16:03 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?



    Results (116 votes). Check out past polls.

    Notices?