Take the 2-minute tour ×

This might seem to be an odd thing to do, but how do I reference a hash while 'inside' the hash itself? Here's what I'm trying to do:

I have a hash of hashes with a sub at the end, like:

my $h = { A => [...], B => [...], ..., EXPAND => sub { ... } };

. I'm looking to implement EXPAND to see if the key C is present in this hash, and if so, insert another key value pair D.

So my question is, how do I pass the reference to this hash to the sub, without using the variable name of the hash? I expect to need to do this to a few hashes and I don't want to keep having to change the sub to reference the name of the hash it's currently in.

share|improve this question
Just a comment, but the [/] brackets are for arrays, not hashes. –  Eric Fossum Jun 24 '13 at 18:18
Also, it appears you have an array of Hashrefs, not a hash. –  Hunter McMillen Jun 24 '13 at 18:18
@darch because there are a number of hashes like this spread out in a bunch of files, I hope adding this sub won't involve another layer of parsing on variable names –  lzt Jun 24 '13 at 18:20
@EricFossum and HunterMcMillen, thanks for the heads up, yeah I'm not particularly familiar with Perl.. –  lzt Jun 24 '13 at 18:23
How do you intend to invoke the sub keyed under EXPAND? Can you simply pass the hashref to the sub when you call it? E.g., for my $h (@please_expand_these_hashrefs) { $h->{EXPAND}->($h); } –  pilcrow Jun 24 '13 at 19:19

3 Answers 3

up vote 4 down vote accepted

What you've got there is some nested array references, not hashes. Let's assume you actually meant that you have something like this:

my $h = { A => {...}, B => {...}, ..., EXPAND() };

In that case, you can't reference $h from within its own definition, because $h does not exist until the expression is completely evaluated.

If you're content to make it two lines, then you can do this:

$h = { %$h, EXPAND( $h ) };
share|improve this answer
(And even if it did exist, nothing's been assigned anything to it yet since you're still building the value to assign to it.) –  ikegami Jun 24 '13 at 18:44

The general solution is to write a function that, given a hash and a function to expand that hash, returns that hash with the expansion function added to it. We can close over the hash in the expansion function so that the hash's name doesn't need to be mentioned in it. That looks like this:

use strict;
use warnings;
use 5.010;

sub add_expander {
    my ($expanding_hash, $expander_sub) = @_;

    my $result = { %$expanding_hash };
    $result->{EXPAND} = sub { $expander_sub->($result) };

    return $result;

my $h = add_expander(
        A => 5,
        B => 6,
    sub {
        my ($hash) = @_;

        my ($maxkey) = sort { $b cmp $a } grep { $_ ne 'EXPAND' } keys %$hash;
        my $newkey = chr(ord($maxkey) + 1);
        $hash->{$newkey} = 'BOO!';

use Data::Dumper;
say Dumper $h;
say Dumper $h;

Notice that we are creating $h but that the add_expander call contains no mention of $h. Instead, the sub passed into the call expects the hash it is meant to expand as its first argument. Running add_expander on the hash on the sub creates a closure that will remember which hash the expander is associated with and incorporates it into the hash.

This solution assumes that what should happen when a hash is expanded can vary by subject hash, so add_expander takes an arbitrary sub. If you don't need that degree of freedom, you can incorporate the expansion sub into add_expander.

share|improve this answer

The hash being built (potentially) happens after EXPAND() runs. I would probably use something like this:

$h = EXPAND( { A=>... } )

Where EXPAND(...) returns the modified hashref or a clone if the original needs to remain intact.

share|improve this answer

Your Answer


