Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Testing for existence of subroutine ref?

by legLess (Hermit)
on Feb 18, 2003 at 03:25 UTC ( [id://236177]=perlquestion: print w/replies, xml ) Need Help??

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

Monks ~

Perl, sadly, is doing what I tell it to, rather than what I want. I'm using a nested hash as a configuration structure; in the code below is a bare-bones example. Most actions within a page use the same config_item (names changed to protect the innocent), but every now and then an action (e.g. the devious "action3" below) needs a different config_item. So I thought I'd add another nesting level and figure out a way to detect those special cases (they happen often enough, and are useful enough, that I'd hate to factor them out). Other than simplification, the only substantive change from my working code is that my subrefs are named rather than anonymous.
my %runmodes = ( config_item => 'regular', action1 => sub{ print "action = 1" }, action2 => sub{ print "action = 2" }, action3 => { config_item => 'special', subroutine => sub{ print "action = 3" }, }, ); sub get_config_item { my( $runmodes, $action ) = @_; my $config_item = $runmodes->{ config_item }; if( exists $runmodes->{ $action }{ subroutine } ) { # error line &{ $runmodes->{ $action }{ subroutine }}; $config_item = $runmodes->{ $action }{ config_item }; } else { &{ $runmodes->{ $action }} } return $config_item; } print ", ", get_config_item( \%runmodes, "action3" ), "\n"; print ", ", get_config_item( \%runmodes, "action1" ), "\n"; # OUTPUT # # action = 3, special # Not a HASH reference at str.pl line 22.
The "exists" test on the error line isn't the first thing I tried, just the most recent, and it's nicely illustrative of what I want to do. I understand why the error's happening, but I can't think of a way to check for that subref without dying (eval, perhaps, but I'm hoping there's another way). I also know I could standardize the hash structure as follows (with attendent changes in the rest of the code, of course):
my %runmodes = ( config_item => 'regular', action1 => { subroutine => sub{ print "action = 1" }}, action2 => { subroutine => sub{ print "action = 2" }}, action3 => { config_item => 'special', subroutine => sub{ print "action = 3" }}, }, );
But two things bug me about that solution. First, those extra "subroutine"s sitting there, redundant, look ugly. Second, regardless of how I solve this particular problem, I still don't know if there's a way to do what I'm trying. Thanks muchly for any assistance.
--
man with no legs, inc.

Replies are listed 'Best First'.
•Re: Testing for existence of subroutine ref?
by merlyn (Sage) on Feb 18, 2003 at 03:38 UTC
    Normally, I hate code that uses ref, but this is precisely what you need here. Something like:
    if (ref $runmodes{$action} eq "CODE") { # it's a coderef, so run it... ... } else { # it's something else {grin} ... }

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

      Why do you hate code that uses ref? Am I missing something?
      Am I posting alot today, or is it just me? (the last bit is just rhetorical, for those who would think to actually answer this, I know it's subjective.)

      feanor_269

        It's often a symptom of poorly organized code if you have to check explicitly what something is in order to determine what to do with it. Ideally you'd want the structure of your code to shape the flow of data/entities such that at any given point you can reliably expect a certain type of thing.

        Imagine walking around with a single instruction "doIt" along with a collection of minor instructions that you had to follow according to what the thingy was to which you were to "doIt". You don't want to have to decide to "drink" from a coffee cup and "drive" a car; rather, you want to be able to rely on the thing you attempt to drink from being a coffee cup, and the thing you hotwire being a car.

        Another way of thinking about it is that it's better to offload decision-making onto the surrounding structure as opposed to navigating an open-ended environment according to a complex rulebook.


        "The dead do not recognize context" -- Kai, Lexx
      Wouldn't UNIVERSAL::isa() suite better?
Re: Testing for existence of subroutine ref?
by djantzen (Priest) on Feb 18, 2003 at 03:43 UTC

    The problem is that you're calling exists on a subroutine reference for actions 1 and 2. Why not test for something else, such as a HASH reference or a CODE reference using ref? What about:

    sub get_config_item { my( $runmodes, $action ) = @_; my $config_item = $runmodes->{ config_item }; if( ref $runmodes->{ $action } eq 'HASH' ) { # error line &{ $runmodes->{ $action }{ subroutine }}; $config_item = $runmodes->{ $action }{ config_item }; } else { &{ $runmodes->{ $action }} } return $config_item; }

    Honestly though, this looks to me like a candidate for refactoring, as the contortions that you're now enduring will only get worse as the code expands over time.


    "The dead do not recognize context" -- Kai, Lexx
Re: Testing for existence of subroutine ref?
by bbfu (Curate) on Feb 18, 2003 at 03:38 UTC

    Change your if line to this, instead:

    if( ref($runmodes->{ $action }) eq 'HASH' ) {

    That should do the trick, no?

    bbfu
    Black flowers blossum
    Fearless on my breath

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (4)
As of 2024-04-18 20:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found