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

I've got the following code snippet:

my %mapping = ( ARRAY => '@', HASH => '%', SCALAR => '$', # GLOB => '?', # REF => '?', # CODE => '?', ); if (grep { $_ eq $type } keys %mapping) { if (isa($_[0], $type)) { return eval "$mapping{$type}{$_[0]}"; } else { return @_; } } else { return (); }

The idea is to allow for someone to pass in either a list or a listref and the function will handle it transparently. (Don't ask why ... it's one of those creature feep things.)

Now, I originally wrote this as a bunch of if-elsif-elsif-else's, but realized that they were all the same, except for the $@% character. (Which brings me to a side question - how do I handle REF, CODE, and GLOB in this fashion?) I figure I should be doing some sort of eval here, but have no idea what.

Help?

Replies are listed 'Best First'.
(tye)Re: Eval and $@% ...
by tye (Sage) on Jun 19, 2001 at 10:01 UTC

    Go back to the if-elsif-else! Stringy eval should be a last resort!

    Besides, treating all of the references the same doesn't make much sense. Sure, I can see wanting to allow both @array and [list], but if you end up wanting to support refs to hashes, then it is unlikely that what you'll want to return is %$href (more likely you'll want to return just the keys or just the values, or have the hash keys have meanings...). And what possible value is there to allow \$value instead of just $value?? Or for letting the user pass in a code reference rather than forcing them to be the one to dereference it?

    Sure, I've written functions where if one of the arguments is a code ref, I take special action. But that special action is never to just call the code in order to get the list. That would be a waste!

    Even for the list vs. ref-to-array cases, I'd usually end up going the other direction: turning the list into an array ref! The whole point of allowing someone to pass in a reference to a list is usually that the list might be quite long and you don't want to have to copy that big list of values just to pass them to some subroutine. But your code just causes lots of extra copying to happen so just don't support refs to anything if that is all you are going to do with them!

    (:

            - tye (but my friends call me "Tye")
      The point to this exercise is to have a function that allows a user to pass in either listrefs or lists to a given function foo(). foo() would then call a function called coerce_args(), passing in what type foo() is expecting (ARRAY, HASH, etc) and @_. coerce_args() would then return back the correct form, if possible.

      I agree that it sounds like there are much better ways of accomplishing this, namely to require the user of foo() to pass in either a hash or hashref. But, once I started down this path, I wanted to finish it in the best Perl-ish fashion.

      Why not use stringy eval? If not stringy eval, then could I use block eval? I'm probably not going to implement something like this (cause I'm going to take your advice, tye, and convert the lists to listrefs), but now I want to find out if what I'm trying is possible and, if not, why not.

        So you want something like this to work:

        my %mapping = ( ARRAY => '@', HASH => '%', SCALAR => '$', GLOB => '*', REF => '$', CODE => '&' ); return eval $mapping{$type}.'{$_[0]}';
        Note that your old code used eval "$mapping{$type}{$_[0]}" which ends up trying to run code like "$SCALAR(0xbadf00)", which doesn't do much good.

        My code above expands out to:

        return @{$_[0]} if $type eq "ARRAY"; return %{$_[0]} if $type eq "HASH"; return ${$_[0]} if $type eq "SCALAR"; return *{$_[0]} if $type eq "GLOB"; return ${$_[0]} if $type eq "REF"; return &{$_[0]} if $type eq "CODE";
        but a more correct version of this useless code would be:
        my( $ref )= @_; $ref= $$ref while UNIVERSAL::isa($ref,"REF"); return @$ref if UNIVERSAL::isa($ref,"ARRAY"); return %$ref if UNIVERSAL::isa($ref,"HASH"); return $$ref if UNIVERSAL::isa($ref,"SCALAR"); return &$ref() if UNIVERSAL::isa($ref,"CODE"); return $$ref,@$ref,%$ref if UNIVERSAL::isa($ref,"GLOB");
        Now if you really want to try to use stringy eval for this, feel free. It seems like a waste to me. And, no, block eval would do you no good in this, unless you use it to make some fatal errors non-fatal (which is what block eval is for), such as using @$ref when $ref is a reference to something other than array.

                - tye (but my friends call me "Tye")
Re: Eval and $@% ...
by jeroenes (Priest) on Jun 19, 2001 at 01:44 UTC
    For starters, you probably would like to read perlref, perlreftut link here, perldsc and perltoot.

    The eval solution is nice. But I don't see where you get the $type from.

    Well, if you have a CODE (it's actually a sort of ref) and the functions are garanteed to neglect the input, you could use CODE => '&'.

    To handle REFs, add:    $type = ref( $_[0] ) if $type eq 'REF';, the eval part doesn't change!

    A GLOB is more problematic. You have to put it into brackets so add an extra if statement:

    if( $type eq 'GLOB' ){ return <$_[0]>; elsif( isa.....

    Hope this helps,

    Jeroen
    "We are not alone"(FZ)

      A GLOB is more problematic. You have to put it into brackets so add an extra if statement...

      You're thinking of glob. The GLOB ref type refers to typeglobs, for which you can just use an asterik.

      So you should just be able to add this to your hash:

      my %mapping = ( #... GLOB => '*', #... );

      bbfu
      Seasons don't fear The Reaper.
      Nor do the wind, the sun, and the rain.
      We can be like they are.

        Look at perlref. GLOBs are Handles in general, so read them in array context with brackets like I did.

        I have never tried to derefence general GLOBs with a '*', aren't they just symbolics like perlref tells us?:

        Symbolic references are names of variables or other objects, just as a symbolic link in a Unix filesystem contains merely the name of a file. The *glob notation is a kind of symbolic reference. (Symbolic references are sometimes called "soft references", but please don't call them that; references are confusing enough without useless synonyms.)

        Jeroen