Home > Net >  Perl Recursive Explanation
Perl Recursive Explanation

Time:10-20

I have following script that need to understand

&main('key_1');

sub main {
    @{$tmp{'key_1'}} = ("A", "B");
    @{$tmp{'A'}} = ("C");

    &test_recurse(\%tmp, 'key_1');
}

sub test_recurse {
    my ($hash, $cell) = @_;
    foreach my $cur_subcell (@{$hash->{$cell}}){
        &test_recurse($hash, $cur_subcell);
    }
    print "X($cell)\n";
}  

The output:

X(C)
X(A)
X(B)
X(key_1)

I want to understand why the key_1 is printing at the last? I am expecting the key_1 might not be printed at all.

CodePudding user response:

I am expecting the key_1 might not be printed at all

The function test_recurse ends with print "X($cell)\n". This means that it ends by printing its second argument. Since you initially call it with key_1 as argument, it prints X(key_1).

To understand a bit better how the function test_recurse works, I suggest to add some prints as follows:

sub test_recurse {
    my ($hash, $cell, $offset) = @_;
    print "${offset}test_recurse($cell)\n";
    foreach my $cur_subcell (@{$hash->{$cell}}){
        &test_recurse($hash, $cur_subcell, $offset . "  ");
    }
    print "${offset}X($cell)\n";
}

Thanks to the addition of $offset, each time you make a recursive call, the prints within this recursive call are indented further to the right. Calling this modified function with test_recurse(\%tmp, 'key_1', ""), you'll get this output:

test_recurse(key_1)
  test_recurse(A)
    test_recurse(C)
    X(C)
  X(A)
  test_recurse(B)
  X(B)
X(key_1)

So, what happens is:

  • You call test_recurse with key_1. This prints test_recurse(key_1). In the foreach loop, it will make two successive calls to test_recurse:

    • The first one with A as argument. This will print test_recurse(A). In the foreach loop, it will make a call to

      • test_recurse with C as argument. This will print test_recurse(C). Since $tmp{C} does not exist, this call does not enter in the foreach loop, and directly proceed to the final print and prints X(C). We then go back to the caller (test_recurse with A as argument).

      Now that the foreach loop is done, this function moves on to the last print and prints X(A). We then go back to the caller (test_recurse with key_1 as argument).

    • The second recursive call is to test_recurse with B as argument. This will print test_recurse(B). Since $tmp{B} does not exist, we do not enter the foreach loop and move on to the final print, which prints X(B). We then return to the caller (test_recurse with key_1 as argument).

    The foreach loop is now over and we move on to the final print, which prints X(key_1).


Some tips:

  • Always add use strict and use warnings at the beginning of your scripts.

  • @{$tmp{'key_1'}} = ("A", "B"); would be clearer as $tmp{'key_1'} = [ 'A', 'B' ].

  • The whole initialization of %tmp could actually be done with:

    my %tmp = (
        key_1 => [ 'A', 'B' ],
        A     => [ 'C' ]
    );
    
  • You call &main('key_1'); with key_1 as argument, but main does not expect any argument.

  • To call a function, you don't need &: do test_recurse(\%tmp, 'key_1'); instead of &test_recurse(\%tmp, 'key_1');.

CodePudding user response:

In a comment, you say:

I am just thinking that as if the variable $cell has been replace by C, A, B why the key_1 is coming back at the end.

And I think that's probably a good indication of where the confusion lies.

Your test_recurse() subroutine starts with this line:

my ($hash, $cell) = @_;

That defines two new variables called $hash and $cell and then populates them from @_. Because these variables are declared using my, they are lexical variables. That means they are only visible within the block of code where they are declared.

Later on in test_recurse() you call the same subroutine again. And, once again, that subroutine starts with the same declaration statement and creates another two variables called $hash and $cell. These two new variables are completely separate from the original two variables. The original variables still exist and still contain their original values - but you can't currently access them because they are declared in a different call to the subroutine.

So when your various calls to the subroutine end, you rewind back to the original call - and that still has the original two variables which still hold their original values.

  • Related