Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

Canon concerning coderef calls?

by jobi (Scribe)
on Nov 15, 2004 at 19:22 UTC ( [id://407916]=perlquestion: print w/replies, xml ) Need Help??

jobi has asked for the wisdom of the Perl Monks concerning the following question:

Masterly Mindful Monks,

The following code is what this novice has managed to glean from reading The Holy Perldoc referencing references.

Now, the question that's eluding the novice's mind is whether 'tis possible to rid the code of that annoying my $coderef = \&$_; line, and do the referencing/dereferencing in one fell swoop?

This novice realizes that this could be done by banishing the line use strict; from the code, but that beautiful flower should really be kept blossoming in the garden of the Monastery, and not be trampled down needlessly.

Peace, Love and Perl,
jobi
#!/usr/bin/perl use warnings; use strict; my @list = qw/one two three/; foreach (@list) { my $coderef = \&$_; &$coderef($_); } sub one { my $number = shift; print "Sub $number reporting for duty.\n" } sub two { my $number = shift; print "Sub $number reporting for duty.\n" } sub three { my $number = shift; print "Sub $number reporting for duty.\n" }

Replies are listed 'Best First'.
Re: Canon concerning coderef calls?
by tmoertel (Chaplain) on Nov 15, 2004 at 19:43 UTC
      Very cool, Tom (I learned something!).

      Here is another alternative:

      foreach my $MySubName (@list) { ## &{\&$MySubName}($MySubName); # This works! ## $::{$MySubName}($MySubName); # This (tmoertel) is better. no strict qw(refs); &{*$MySubName}($MySubName); # Use GLOB - Also cool! #Chromatic's OO way -- #can(METHOD) # can checks to see if its object has a method called METHOD, # if it does then a reference to the sub is returned, i # if it does not then undef is returned. # main->can($MySubName)->($MySubName); }
      Update:Added Chromatic's OO-Style + Doc

          Earth first! (We'll rob the other planets later)

        Chromatic's OO way

        I wouldn't really call that an OO way. Sure, it uses can as a class method on main, but that's where the OOishness ends. It returns a plain coderef and executes it with a different dereferencing syntax than you used. I believe it is also the only alternative that does not use a symbolic reference.

        To reinforce the point about being a plain coderef, this works:

        foreach (@list) { my $coderef = main->can($_); &$coderef($_); }

        And this does too, if you really want to get rid of all the pointy arrows:

        foreach (@list) { my $coderef = UNIVERSAL::can('main', $_); &$coderef($_); }

        Update: and if you want to see what I would consider the "OO way":

        foreach (@list) { main->$_($_); }

        But, frankly, I think that's very ugly. It uses a symbolic reference too. I would use can as chromatic originally did.

      This works beautifully, but this novice is at a loss to understand why.

      Is not $:: an indicator that the sub is in the main package? So that $::{$_} is the subroutine $_ in the main package? And then that is called with the arguments $_?

      Why does this allow us to do the referencing/dereferencing in one step, is this novice missing something here?

        Yes - you have understood the sequence correctly.

        The relevant document is the "Symbol Table" section of perlmod.

        The symbol table for a package happens to be stored in the hash of that name with two colons appended. The main symbol table's name is thus %main::, or %:: for short. ...

        Hence "$::" is de-referencing the "%main::" hash, using "$_" as the key. The following parens prvide the "sub" context, and allow the sub to be passed the $_ param.

            Earth first! (We'll rob the other planets later)

Re: Canon concerning coderef calls?
by chromatic (Archbishop) on Nov 15, 2004 at 19:54 UTC
    main->can( $_ )->( $_ ) for (qw( one two three ));
Re: Canon concerning coderef calls?
by pg (Canon) on Nov 15, 2004 at 19:23 UTC

    Store the code ref itself, not the names in your list.

    use warnings; use strict; my @list = (\&one, \&two, \&three); for (0 .. $#list) { &{$list[$_]}($_); } sub one { my $number = shift; print "Sub $number reporting for duty.\n" } sub two { my $number = shift; print "Sub $number reporting for duty.\n" } sub three { my $number = shift; print "Sub $number reporting for duty.\n" }
      Alas, in the specific project I am working on (not the example code) the list is not for me to create, just to use.

      Still, thank you for your wisdom.
      jobi

      my @list = (\&one, \&two, \&three);

      my @list = \(&one, &two, &three);
      :-)

      ihb

      See perltoc if you don't know which perldoc to read!
      Read argumentation in its context!

Re: Canon concerning coderef calls?
by calin (Deacon) on Nov 16, 2004 at 02:06 UTC

    Do yourself a favour and implement a dispatch table. This additional level of indirection may seem a maintenance burden, but it will sure pay off in the long run. You will know exactly when and why it's failing, and you will also minimize the risk of malicious code injection.

    Here's a sketch:

    #!/usr/bin/perl use strict; use warnings; use Carp; { my %actions = ( one => \&one, two => \&two, three => \&three ); sub dispatch { # detect early all invalid arguments my @invalid = grep {!exists($actions{$_})} @_; if (@invalid) { local $" = ', '; croak("invalid action(s): (@invalid)"); # or the death spell of your choice } # else { for (@_) { # call $actions{$_}->(...), for example: $actions{$_}->("<<$_>>"); } # } } } # stub subs generator for exemplification only BEGIN { eval 'sub ' . $_ . q{ { local $" = ', '; print "sub [@{[(caller 0)[3]]}] with args: (@_)\n"; }} for qw/one two three/; } dispatch(qw/one two three/); dispatch(qw/three two one kaboom silence/);

    Don't just cut and paste my code. I don't have the slightest idea about what you're trying to do with your program. The code above is not substitute for your own judgement.

    P.S. If you decide to use symbol tables instead of plain hashes for lookup, as other people have suggested in this thread, try to use a separate namespace for this purpose and not main::. "Whitelisting" is important from a security perspective.

      Thank you for your sage advice, this novice did have in mind to implement some form of dispatch table in the actual project code, but your sketch makes a few points this novice most certainly wouldn't have thought about on his own.

      jobi
Re: Canon concerning coderef calls?
by hv (Prior) on Nov 15, 2004 at 23:34 UTC

    You want to call a subroutine by the name in a variable, which is disallowed by strict 'refs'. One of the values of strictness is that if strictness is turned off in one part of some code that is otherwise compliant, it is a useful warning to the reader that dangerous stuff is happening here - rather than hide it, highlight it:

    for my $subname (@list) { # we have to turn off strict 'refs' here because ... # but it is safe to do so because ... no strict 'refs'; $subname->($subname); }

    Hugo

Re: Canon concerning coderef calls?
by NetWallah (Canon) on Nov 15, 2004 at 19:32 UTC
    Clunky, but this works ...
    foreach my $MySubName (@list) { &{\&$MySubName}($MySubName); }

        Earth first! (We'll rob the other planets later)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://407916]
Approved by pg
Front-paged by CountZero
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (5)
As of 2024-03-19 08:46 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found