Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Behaving appropriately based on ref() result

by qbxk (Friar)
on Jan 08, 2006 at 14:33 UTC ( #521818=perlquestion: print w/replies, xml ) Need Help??

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

Writing a function to traverse a "configuration" array I have a series of if/elsifs that switch based on what kind of data it encounters, ie. by whatever ref() returns.

here's an mini-example:

my $ref = ref($_); if( $ref eq 'Regexp' ) { #ah, we are matching a match } elsif( $ref eq 'CODE') { #sombody slipped a sub{} in here, ok } elsif( $ref eq 'ARRAY') { #an @array we have found } elsif( $ref eq 'HASH') { #a %hash, obviously } elsif( $ref eq '') { #must be a $scalar } else { warn "Unknown ref() wth is a $ref ?!"; }

My question for the clergy is: what's a more elegant way to accomplish this? or what is your favorite idiom for this task?

also, have i hit on all the possible values that ref() can return?


It's not what you look like, when you're doin' what you’re doin'.
It's what you’re doin' when you’re doin' what you look like you’re doin'!
     - Charles Wright & the Watts 103rd Street Rhythm Band

Replies are listed 'Best First'.
Re: Behaving appropriately based on ref() result
by esskar (Deacon) on Jan 08, 2006 at 14:46 UTC
    use a Hash
    my %RefTypes = ( 'Regexp' => \&HandleRegexp, 'CODE' => \&HandleCodeRef, 'ARRAY' => \&HandleArrayRef, 'HASH' => \&HandleHashRef, 'SCALAR' => \&HandleScalarRef, '' => \&HandleScalar, ); my $ref = ref($_); if(exists $RefTypes{$ref}) { $RefTypes{$ref}->($_); } else { warn "Unknown ref() wth is a $ref ?!"; }
    HTH
      Just for reference using a hash this way is called a dispatch table.

                      - Ant
                      - Some of my best work - (1 2 3)

Re: Behaving appropriately based on ref() result
by tirwhan (Abbot) on Jan 08, 2006 at 14:55 UTC

    You could also use the reftype function from Scalar::Util. This has the advantage of

    1. Explicitly returning undef on a non-reference variable
    2. Returning the underlying data structure type when handed an object

    for example:

    use Scalar::Util qw(reftype); my $o=bless {},MyClass; print ref $o # prints "MyClass" print reftype($o) # prints "HASH"

    There are ten types of people: those that understand binary and those that don't.
Re: Behaving appropriately based on ref() result
by dragonchild (Archbishop) on Jan 08, 2006 at 16:22 UTC
    You forgot at least 2-3 possible return values, including SCALARREF. Check out ref for a more complete list. Data::Dumper may also be a good place to look for how to handle this.

    But, I would rethink what you're doing if you have to do this. Especially, if you're doing this for configuration purposes. How on earth are you planning on persisting a CODE or Regexp reference in your configuration?


    My criteria for good software:
    1. Does it work?
    2. Can someone else come in, make a change, and be reasonably certain no bugs were introduced?

      Data::Dump::Streamer can be used to serialize subroutines and closures properly under sane scenarios. (Yes you can deliberately craft a set of closures that will not dump properly, but it should be quite rare in practice as it would require using unwise var naming practices in the first place.)

      As for regular expressions, DDS can serilize them properly as well so long as they don't include (?{}) and (??{}). In other words regexes that arent closures are properly handled. Ill have to make a stab at handling regex closures in a future release.

      PS: i think you meant "SCALAR" and "REF", there is no perl type "SCALARREF". :-)

      ---
      $world=~s/war/peace/g

Re: Behaving appropriately based on ref() result
by McDarren (Abbot) on Jan 08, 2006 at 14:44 UTC
Re: Behaving appropriately based on ref() result
by demerphq (Chancellor) on Jan 09, 2006 at 09:04 UTC

    When i have had to do this its looked much like you have posted. However as others have pointed out you will have issues with using ref() for this task. And unfortunately reftype() isn't a complete solution to the problem as 'Regexp' is unfortunately not treated as a native Perl type but rather as a blessed scalar reference. Thus reftype(qr/./) will return SCALAR not "Regexp".

    In Data::Dump::Streamer I provide some utility XS methods that make this job a little easier. Data::Dump::Streamer::reftype() is similar to that provided by Scalar::Util except it returns PL_no (false) instead of undef which means you don't have to bother with a checking if the response is defined. A simple (if reftype($foo) eq 'SCALAR') will not raise a warning.

    A second utility function of interest is Data::Dump::Streamer::regex() which in list context returns a list containing the regex pattern (without quoting or '(?:)' wrapper) and the modifiers on the pattern, and in scalar context returns the same as a stringified qr// would. This routine can not be fooled by blessing the qr// or by overloading its stringification logic (which says to me that regex's are native types distinct from normal SCALAR's and that they should receive a unique reftype designation)

    So the logic would look something like this:

    use Data::Dump::Streamer qw(reftype regex); my $type=reftype($obj); if ($type eq 'SCALAR' and my $re=regex($obj)) { # Its a regex, $re contains the unquoted pattern } elsif ($type eq 'REF' or $type eq 'SCALAR' or $type eq 'GLOB') { # its something that can be derefed by $$obj } elsif ( $type eq 'HASH' ) # its a hash } elsif ( $type eq 'ARRAY' ) # its an array } elsif ( $type eq 'CODE' ) # its code ref } elsif ( $type eq 'FORMAT' ) # its a format ref } elsif ( $type) { warn "Unhandled type: '$type'\n"; } else { # its not a reference at all }

    For my uses I never found a dispatch table to be worthwhile. I think the overhead of doing the lookup and dispatch is more than doing the string comparisons in this case. For a larger dispatch table it might be worth while tho. You'd have to benchmark to see.

    ---
    $world=~s/war/peace/g

      Of course, the overhead difference is hardly such that your typical Perl program would suffer, but certainly if you are being speed conscious. Especially since you can arrange your constructs so that the most common options come first.

      I often prefer dispatch for the ease of adding new items.

                      - Ant
                      - Some of my best work - (1 2 3)

Re: Behaving appropriately based on ref() result
by bsb (Priest) on Jan 09, 2006 at 07:52 UTC
    Data::Rmap has code doing something like this and may meet your needs. It just uses Scalar::Utils::reftype and if/elses.

    ref() can return anything for blessed objects

    perl -le 'print ref(bless{},"Hello World")'

    Scalar util's reftype is better for this job.

      Except that it (Scalar::Util::reftype()) can't tell a regexp from a ref to a scalar.

      ---
      $world=~s/war/peace/g

A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (3)
As of 2023-02-07 04:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    I prefer not to run the latest version of Perl because:







    Results (38 votes). Check out past polls.

    Notices?