Perl 递归解释

Perl Recursive Explanation

我有以下脚本需要理解

&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";
}  

输出:

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

我想明白为什么key_1在最后打印?我预计 key_1 可能根本不会打印出来。

I am expecting the key_1 might not be printed at all

函数 test_recurseprint "X($cell)\n" 结束。这意味着它以打印第二个参数结束。由于您最初使用 key_1 作为参数调用它,因此它会打印 X(key_1).

为了更好地理解函数 test_recurse 的工作原理,我建议添加一些打印如下:

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";
}

由于添加了 $offset,每次进行递归调用时,此递归调用中的打印都会进一步向右缩进。使用 test_recurse(\%tmp, 'key_1', "") 调用这个修改后的函数,你会得到这个输出:

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

所以,发生的事情是:

  • 您用 key_1 调用 test_recurse。这会打印 test_recurse(key_1)。 在 foreach 循环中,它将连续两次调用 test_recurse:

    • 第一个以 A 作为参数。这将打印 test_recurse(A)。 在 foreach 循环中,它会调用

      • test_recurseC 作为参数。这将打印 test_recurse(C)。 由于$tmp{C}不存在,所以本次调用不进入foreach循环,直接进行最后的打印,打印X(C)。然后我们回到调用者(test_recurseA 作为参数)。

      现在 foreach 循环已经完成,此函数将继续执行最后一个 print 并打印 X(A)。然后我们回到调用者(test_recursekey_1 作为参数)。

    • 第二个递归调用是 test_recurse,参数是 B。这将打印 test_recurse(B)。由于 $tmp{B} 不存在,我们不进入 foreach 循环并继续打印 X(B) 的最终打印。然后我们 return 给调用者(test_recursekey_1 作为参数)。

    foreach 循环现在结束,我们继续最后的 print,它打印 X(key_1).


一些小技巧:

  • 始终在脚本开头添加 use strictuse warnings

  • @{$tmp{'key_1'}} = ("A", "B"); 会比 $tmp{'key_1'} = [ 'A', 'B' ].

    更清楚
  • %tmp 的整个初始化实际上可以通过以下方式完成:

    my %tmp = (
        key_1 => [ 'A', 'B' ],
        A     => [ 'C' ]
    );
    
  • 您使用 key_1 作为参数调用 &main('key_1');,但 main 不期望任何参数。

  • 要调用函数,您不需要 &:执行 test_recurse(\%tmp, 'key_1'); 而不是 &test_recurse(\%tmp, 'key_1');

在评论中,您说:

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.

而且我认为这可能很好地表明了混淆所在。

您的 test_recurse() 子例程从这一行开始:

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

定义了两个新变量 $hash$cell,然后从 @_ 填充它们。因为这些变量是使用 my 声明的,所以它们是 词法 变量。这意味着它们仅在声明它们的代码块中可见。

稍后在 test_recurse() 中您再次调用相同的子例程。并且,该子例程再次以相同的声明语句开始,并创建 另一个 两个名为 $hash$cell 的变量。这两个新变量与原来的两个变量完全分开。原始变量仍然存在并且仍然包含它们的原始值 - 但您目前无法访问它们,因为它们是在对子例程的不同调用中声明的。

因此,当您对子例程的各种调用结束时,您将返回到原始调用 - 并且仍然具有原始的两个变量,它们仍然保持其原始值。