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

I used the following subs in a program to dereference hash and array refs...

# dereference an array or an hash sub de_ref { return map { ref $_ eq 'ARRAY' ? de_aref( $_ ) : ref $_ eq 'HASH' ? de_aref( de_href($_) ) : $_ } @_; } # dereference an array ref, if any sub de_aref { return map {ref $_ eq 'ARRAY' ? (@{$_}) : ($_)} @_; } # dereference an hash ref, if any sub de_href { return map {ref $_ eq 'HASH' ? (sort values %$_) : ($_) } @_; }

...i was wondering if anybody has any comments on it. One improvment that is most obivious is using proper recursion (which i am working on (soft of)) in de_ref().

Replies are listed 'Best First'.
Re: dereferencing hash & array refs
by pg (Canon) on Feb 09, 2003 at 07:36 UTC
    Recursive code with a little piece of testing data:
    use strict; my @result; my @a = (1, 2, [3, [4, 5]], 6, [7, 8, [9,10], [11, 12]], {1=>13, 2=>{2 +=> 14}}); de_ref(\@result, \@a); print(join(",",@result));#expect 1,2,3,4,5,6,7,8,9,10,11,12,13,14 sub de_ref { my ($result, $source) = @_; if (!ref($source)) { push @{$result}, $source; } else { if (ref($source) eq "ARRAY") { foreach my $ele (@{$source}) { de_ref($result, $ele); } } elsif (ref $source eq "HASH") { foreach my $ele (values %{$source}) { de_ref($result, $ele); } } elsif (ref $source eq "SCALAR") { push @{$result}, $$source; } } }
    Two reminders about this piece of code:
    1. ref of types other than scalar, array and hash are ignored (those being ignored include REF, CODE, and GLOB), as it looks like you only want those three types
    2. assume that there is no circular ref

      I did not imply that somebody does the work for me. But now that pg had already done that (create the recursive version), i can only thank him.

Re: dereferencing hash & array refs
by demerphq (Chancellor) on Feb 09, 2003 at 14:46 UTC
    Besides the optimization that pg presents (that of passing a ref to the array into the subs and not returning lists), there is danger that you want to consider. That being that ref $foo eq "TYPE" is a dangerous test. It will work with plain vanilla data structures, but will choke if there are blessed refs involved. UNIVERSAL::isa($foo,'TYPE') is safer as it doesnt suffer from this problem.

    Also a minor technicality, there are more forms of SCALAR ref than just SCALAR. REF is for instance a scalar (it is a reference to a scalar that holds a reference itself), as is GLOB.. By this i mean that they are dereferenced in the same way that a scalar is. The special SCALAR ref type 'Regexp' will incidentally pass the UNIVERSAL::isa(qr//,"SCALAR") test but is handled very differently (but thats a can of worms you probably want to avoid :-)

    my $scalar=\"Foo"; my $REF=\$scalar; my $GLOB=\*A; print "$$scalar $scalar $$REF $$GLOB\n"; __END__ Foo SCALAR(0x1ab3108) SCALAR(0x1ab3108) *main::A

    --- demerphq
    my friends call me, usually because I'm late....

      Thanks for bringing up a very good point about various SCALAR types of references which i hadn't given much thought before.

      And i am aware of the cost of passing complete lists (arrays and hashes) and long strings to/from a sub. I am/was not concerned much about that for the particular program.

Re: dereferencing hash & array refs
by steves (Curate) on Feb 09, 2003 at 06:43 UTC

    Parts of this confuse me ... for any hashes you only care about the values? Why use a hash in the first place then? There may be a reason in the larger context of your program I'm missing. Why do the individual dereferencing functions use map when they're only ever passed scalars?

    I guess my bottom line question is what is it that just prevents you from doing this:

    my @a = @$a_ref; # I know it's an array my %h = %$h_ref; # I know it's a hash my @h = values(%$h_ref); # I just want the values # Bascically what you're doing above. my @a = (ref($x) eq 'ARRAY') ? @$x : values(%$x);
      The program is going to be a wrapper around another to be executed via system(). Arguments passed to system() are set via command line options/arguments (w/ help from Getopt::Long), which are stored in a hash. Ergo, as far as system() is concerned, all it needs to see are the values (of the hash).