0

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.

shikhanshu
  • 1,466
  • 2
  • 16
  • 32
  • 1
    There's nothing wrong with the code you shown. Copied and pasted, added imports, removed your c style comments, (use # for perl comments) and it printed as expected. Can you show your exact code? Are you using strict and warniings? – Nate Nov 21 '14 at 01:24
  • I suspect the problem comes when you try to *use* the hashref; show what you are trying there. http://perlmonks.org?node=References+quick+reference has some helpful rules to figure out how to use a reference – ysth Nov 21 '14 at 01:40

2 Answers2

2

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}.
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • Perfect. I did realize that the return was the problem. But didn't think to return from the foreach loop, which really is the last statement executing in the subroutine. – shikhanshu Nov 21 '14 at 04:23
  • Thanks for the help! What does & do for subroutine calls? I have always used them. I read this link to get more explanation: http://stackoverflow.com/questions/1347396/when-should-i-use-the-to-call-a-perl-subroutine and I won't be using it anymore! – shikhanshu Nov 21 '14 at 17:43
  • Causes the prototype to be ignored. – ikegami Nov 21 '14 at 18:01
0

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!

shikhanshu
  • 1,466
  • 2
  • 16
  • 32