Perl: Returning hashref from subroutine

739 views Asked by At

I have a hash that is a deeply nested structure. The level of nesting is not known before-hand. But each level has two properties "instance" and another hash of "dependencies". So it's a kind of recursive-looking hash.

my $HASH = {
    "top"=> {
        "instance" => "top_instance",
        "dependencies" => {
            "sub_block1" => {
                "instance" => "sub_block1_instance",
                "dependencies" => {}
            },
            "sub_block2" => {
                "instance" => "sub_block2_instance",
                "dependencies" => {
                    "sub_block3" => {
                        "instance" => "sub_block3_instance",
                        "dependencies" => {}
                    }
                }
            }
        }
    }
};

I have a subroutine which accepts a user-defined string and returns an internal piece of the hash from the specified level of hierarchy.

Example, if user specifies "sub_block2", the subroutine should return this hash:

{
    "sub_block2" => {
        "instance" => "sub_block2_instance",
        "dependencies" => {
            "sub_block3" => {
                "instance" => "sub_block3_instance",
                "dependencies" => {}
            }
        }
    }
}

This is my sub-routine:

sub get_starting_point {
    my $string = shift;
    my $config = shift;
    foreach my $key (keys %$config) {
        if($key ne $string) {
            # if current key is not what user asked for, recurse into next level of hierarchy
            if (exists $$config{$key}{dependencies}) {
                &get_starting_point($$config{$key}{dependencies});
            }
        } else {
            # we found the key, return the hash under this hierarchy
            my $tempHash = {$key => $$config{$key}};
            print ref($tempHash); # correctly prints HASH
            print Dumper($tempHash); # correctly prints out the sub-hash
            return $tempHash; # I am expecting this return value to be a hash-ref
        }
    }
}

As you can see, this is a recursive function, which keeps diving down deep in the hash until it hits a key which matches the argument and returns the complete sub-hash under that key.

This is how I am calling this sub-routine.

my $subHash = get_starting_point("sub_block2",$HASH);
print ref($subHash); # is not a ref
print Dumper($subHash); # prints nothing

What am I doing wrong!?!?

EDIT: Updated the question with my exact problem. Seems like a simple example I used before worked as expected.

2

There are 2 answers

3
ikegami On

You are returning the value to which a foreach evaluates (by virtue of being the last statement of the sub). It's not surprising that's not a reference.

&get_starting_point($$config{$key}{dependencies});

should be

my $rv = get_starting_point($config->{$key}{dependencies});
return $rv if $rv;

And add return undef; at the end rather than relying on foreach to return something false.

sub get_starting_point {
    my $string = shift;
    my $config = shift;
    for my $key (keys %$config) {
        if ($key eq $string) {
            return { $key => $config->{$key} };
        }

        if ($config->{$key}{dependencies}) {
           my $rv = get_starting_point($config->{$key}{dependencies});
           return $rv if $rv;
        }
    }

    return undef;
}

Notes:

  • Please don't prefix your sub calls using &. Do you even know what that does?
  • Most people find $config->{$key} more readable than $$config{$key}.
1
shikhanshu On

Ouch. I was assuming that the "return" statement in the recursive function immediately breaks out of the recursion and returns the value. It doesn't. So when the recursion goes as deep as it can, and starts coming back up, there is nothing to return anymore.

I solved this by setting a global outside the subroutine, and within the sub-routine, I am simply setting this global to the sub-hash.

That serves my purpose!