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

Hi all,

After I passed a hash ref to a subroutine, how can I see what the hash name was? i.e.:
my %hash1 = ( one => 1, two => 2 ); check_limit(\%hash1); sub check_limit { my $hname = $_[0]; my $num = 0; for (keys %{$hname}) { $num++ }; print "Hash: $hname has $num keys\n"; }
Result:
Hash: HASH(0x297f8) has 2 keys
The reason I don't pass the whole hash is because these hash-puppies can get quite large and I'd like to avoid copying them. This is just an example of a problem I'm experiencing writing a larger program.

Thanks beforehand.
Cheers....

Replies are listed 'Best First'.
Re: Getting a Hash Name
by jonadab (Parson) on Jul 04, 2005 at 12:31 UTC

    A hash reference doesn't have a name. References in general don't have names. Sure, you can take a reference to a named object, like you are doing, but you can also take a reference to an anonymous object, like this:

    check_limit( +{ one => 1, two => 2 });

    This has exactly the same semantics, as far as the subroutine is concerned, as the way you are doing it. Either way, you are passing a reference to a hash, so the subroutine gets a reference that points to a hash data structure. But it does not get a name, because references are anonymous. If you want to pass the *name* of the hash to the subroutine, you'll have to accomplish that another way, e.g., by passing the name as a string, or by passing a glob, or whatever.

    The reason I don't pass the whole hash is because these hash-puppies can get quite large and I'd like to avoid copying them.

    Passing a copy of the hash doesn't pass the name either; it just passes (a copy of) the contents. Your subroutine gets to set up its own name for the contents, as in:

    sub check_limit { my %happyhash = @_; # It is now named %happyhash within the subroutine. my $num = keys %happyhash; print "Hash: happyhash has $num keys\n"; }

    You can also do this if the hash is passed by reference, but the syntax is slightly different:

    sub check_limit { my %happyhash = %{$_[0]}; # It is now named %happyhash within the subroutine. my $num = keys %happyhash; print "Hash: happyhash has $num keys\n"; }

    So now you have your own name, within the subroutine, for the thing. The calling code that passed it to you may have had a different name, or the same name, or no name at all, or more than one name, for the same thing, but you don't know that, because that information is not passed. If you need that information, you have to get it another way, such as by having the calling code pass that in separately, as someone else suggested.

    Also note that if the hash is large, you could significantly improve the efficiency of your subroutine by calling the builtin keys in numeric context to get the number of keys, rather than calling it in list context and looping over the list to count up to the same number. I'm not sure if that matters, since I'm not sure if your subroutine is real or a made-up example to illustrate your other question, but I thought I'd mention it.


    "In adjectives, with the addition of inflectional endings, a changeable long vowel (Qamets or Tsere) in an open, propretonic syllable will reduce to Vocal Shewa. This type of change occurs when the open, pretonic syllable of the masculine singular adjective becomes propretonic with the addition of inflectional endings."  — Pratico & Van Pelt, BBHG, p68
Re: Getting a Hash Name
by holli (Abbot) on Jul 04, 2005 at 11:21 UTC
    how can I see what the hash name was
    You can't. And honestly, I don't know why you would need that information.

    If you need more name info, use a hash of hashes.


    holli, /regexed monk/
Re: Getting a Hash Name
by polettix (Vicar) on Jul 04, 2005 at 11:27 UTC
    One of the problems I see with this kind of reflection is that one hash could have more than one name if you create aliases using GLOBs:
    my *otherName = %hash1;
    If you pay the maximum attention, you could pass the name of the hash as a scalar string, then use eval variable name dereferencing (like %$name, as pointed out below by others) inside check_limit to access the actual hash. Did I already say that you have to carefully check every implication of such an approach?

    Update: I realised that eval is overkill in this case, a simple %$name will do as suggested by others. Nonetheless, this must not be an excuse to drop a careful approach.

    Flavio
    perl -ple'$_=reverse' <<<ti.xittelop@oivalf

    Don't fool yourself.
      One of the problems I see with this kind of reflection is that one hash could have more than one name

      Moreover, it does not even need to have a name at all:

      check_limit({ one => 1, two =>2 });
Re: Getting a Hash Name
by Dietz (Curate) on Jul 04, 2005 at 11:38 UTC
    So why not simply extend your subroutine:

    my %hash1 = ( one => 1, two => 2 ); check_limit(\%hash1, '%hash1'); sub check_limit { my $href = $_[0]; my $name = $_[1]; my $num = 0; for (keys %{$href}) { $num++ }; print "Hash '$name' has $num keys\n"; }
Re: Getting a Hash Name
by Hena (Friar) on Jul 04, 2005 at 11:46 UTC
    I would put the print into two sections.
    print "Hash: hash1" check_limit(\%hash1); sub check_limit { my $hname = $_[0]; my $num = 0; for (keys %{$hname}) { $num++ }; print " has $num keys\n"; }
    Or then use another variable to give the name to subroutine.
Re: Getting a Hash Name
by salva (Canon) on Jul 04, 2005 at 12:06 UTC
    you can use a glob for that, though usually it is a bad idea to do so:
    sub check_limit (\*) { my $glob = shift; my $name = *{$glob}; $name=~/^\*/%/; my $num = keys %{*$glob}; print "Hash: $name has $num keys\n"; } our %foo = qw(q w e t y j f f); check_limit(*foo);
Re: Getting a Hash Name
by TomDLux (Vicar) on Jul 04, 2005 at 15:34 UTC

    If you want the hash name for debugging messages and that sort of thing, pass the name as an additional variable.

    But what I want to talk about is the body of your subroutine. keys ${$hname} returns a list. Instead of going through the list item by item, in good C style, why not use idiomitic Perl: if you assign a list to a sscalar, the variable is assigned the number of elements in the list.

    sub check_limit { my ( $href, $name ) = $_; $name ||= '<unnamed>'; # assign text if not provided my $num = keys %{$href}; print "Hash: $hname has $num keys\n"; }
    Tom

    --
    TTTATCGGTCGTTATATAGATGTTTGCA

Re: Getting a Hash Name
by magnus (Pilgrim) on Jul 04, 2005 at 11:46 UTC
    Maybe it's not sexy and it's a bit simple but why don't you just var the name and then pass it through, like:

    my $name = "HASH"; my %$name = ( one => 1, two => 2 ); check_limit($name); sub check_limit { my ($hname) = $_[0]; my $num = 0; for (keys %{$hname}) { $num++ }; print "Hash: $hname has $num keys\n"; }


    I've done similar things on very long pieces of code for debugging purposes...

    magnus

      Why not? Because it doesn't pass strict, which can be a problem with longer programs. Having global variables pop in and out of existence is difficult to debug.

Re: Getting a Hash Name
by Anonymous Monk on Jul 05, 2005 at 01:52 UTC

    The reason I don't pass the whole hash is because these hash-puppies can get quite large and I'd like to avoid copying them.

    As an aside, this statement suggests you don't know that passing a reference doesn't copy the data structure at all, it passes a copy of the reference to the data. So there is no problem with passing a large hash around like this, except its then easy for any part of the code to modify data that you might prefer be constant.

    perl -e 'sub f{print "f($_[0])\n"}; $h={one=>1,two=>2}; print "$h\n"; +f($h);' HASH(0x80f57ac) f(HASH(0x80f57ac))