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_recurse
以 print "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_recurse
以 C
作为参数。这将打印 test_recurse(C)
。
由于$tmp{C}
不存在,所以本次调用不进入foreach
循环,直接进行最后的打印,打印X(C)
。然后我们回到调用者(test_recurse
以 A
作为参数)。
现在 foreach
循环已经完成,此函数将继续执行最后一个 print
并打印 X(A)
。然后我们回到调用者(test_recurse
以 key_1
作为参数)。
第二个递归调用是 test_recurse
,参数是 B
。这将打印 test_recurse(B)
。由于 $tmp{B}
不存在,我们不进入 foreach
循环并继续打印 X(B)
的最终打印。然后我们 return 给调用者(test_recurse
以 key_1
作为参数)。
foreach
循环现在结束,我们继续最后的 print
,它打印 X(key_1)
.
一些小技巧:
始终在脚本开头添加 use strict
和 use 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
的变量。这两个新变量与原来的两个变量完全分开。原始变量仍然存在并且仍然包含它们的原始值 - 但您目前无法访问它们,因为它们是在对子例程的不同调用中声明的。
因此,当您对子例程的各种调用结束时,您将返回到原始调用 - 并且仍然具有原始的两个变量,它们仍然保持其原始值。
我有以下脚本需要理解
&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_recurse
以 print "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_recurse
以C
作为参数。这将打印test_recurse(C)
。 由于$tmp{C}
不存在,所以本次调用不进入foreach
循环,直接进行最后的打印,打印X(C)
。然后我们回到调用者(test_recurse
以A
作为参数)。
现在
foreach
循环已经完成,此函数将继续执行最后一个print
并打印X(A)
。然后我们回到调用者(test_recurse
以key_1
作为参数)。第二个递归调用是
test_recurse
,参数是B
。这将打印test_recurse(B)
。由于$tmp{B}
不存在,我们不进入foreach
循环并继续打印X(B)
的最终打印。然后我们 return 给调用者(test_recurse
以key_1
作为参数)。
foreach
循环现在结束,我们继续最后的print
,它打印X(key_1)
.
一些小技巧:
始终在脚本开头添加
use strict
和use 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 thekey_1
is coming back at the end.
而且我认为这可能很好地表明了混淆所在。
您的 test_recurse()
子例程从这一行开始:
my ($hash, $cell) = @_;
定义了两个新变量 $hash
和 $cell
,然后从 @_
填充它们。因为这些变量是使用 my
声明的,所以它们是 词法 变量。这意味着它们仅在声明它们的代码块中可见。
稍后在 test_recurse()
中您再次调用相同的子例程。并且,该子例程再次以相同的声明语句开始,并创建 另一个 两个名为 $hash
和 $cell
的变量。这两个新变量与原来的两个变量完全分开。原始变量仍然存在并且仍然包含它们的原始值 - 但您目前无法访问它们,因为它们是在对子例程的不同调用中声明的。
因此,当您对子例程的各种调用结束时,您将返回到原始调用 - 并且仍然具有原始的两个变量,它们仍然保持其原始值。