我的子程序不会递归的原因
Reason my subroutine won't recurse
#!/usr/bin/perl
use strict;
use warnings;
use List::MoreUtils 'uniq';
my %functiontable =();
$functiontable{foo} = \&foo;
sub iterate {
my ($function, $iterations, $argument) = @_;
return $argument unless 0 < $iterations;
return $argument unless $function = $functiontable{$function};
my @functioned = $function->($argument);
my @refunctioned = ();
for my $i (0 .. @functioned - 1) {
push @refunctioned, iterate ($function, ($iterations - 1), $functioned[$i]);
}
return uniq @refunctioned;
}
sub foo {
my ($argument) = @_;
my @list = ($argument, $argument.'.', $argument.',');
return @list;
}
my @results = iterate 'foo', 2, 'the';
print "@results";
这会打印 the the. the,
,即它不会迭代(递归)。我希望它打印 the the. the, the.. the., the,. the,,
.
(我用Smart::Comments检查它是否第二次进入iterate
,确实进入了,但它似乎并没有完成函数中的所有事情。)
我不明白为什么。有人可以帮我找出原因,或提出修复建议吗?
这一行:
return $argument unless $function = $functiontable{$function};
没有意义。在您的子例程 iterate
中,$function
是一个字符串,而 $functiontable{$function}
是对子例程的引用。我不确定这样做的目的是什么:是为了与存储的函数进行比较吗?是使用名字$function
引用的函数吗?
假设是后者,在调用迭代时简单地传递对函数的引用会更有意义:
sub iterate {
my ($function, $iterations, $argument) = @_;
return $argument unless 0 < $iterations;
my @functioned = $function->($argument);
my @refunctioned = ();
for my $i (0 .. @functioned - 1) {
push @refunctioned, iterate ($function, ($iterations - 1), $functioned[$i]);
}
return uniq @refunctioned;
}
my @results = iterate($functiontable{foo}, 2, 'the');
print "@results";
输出:
the the. the, the.. the., the,. the,,
第一次调用子例程 iterate
时,它会将 $function
中的子例程名称从名称转换为子例程引用
所以第一次 iterate
调用自身时它正在传递子例程引用,并且行
return $argument unless $function = $functiontable{$function};
将引用字符串化并尝试使用类似于 CODE(0x23e0838)
的键来查找散列的元素
显然该元素不存在,因此您的 unless
失败并立即返回 $argument
而没有继续递归
更新
我会写这样的东西
#!/usr/bin/perl
use strict;
use warnings;
use 5.10.0;
my %functions = ( foo => \&foo );
sub iterate {
my ($func, $arg, $depth) = @_;
return $arg unless $depth;
map {iterate($func, $_, $depth - 1); } $functions{$func}->($arg);
}
sub foo {
my ($arg) = @_;
map "$arg$_", '', '.', ',';
}
my @results = iterate('foo', 'the', 2);
say "@results";
输出
the the. the, the. the.. the., the, the,. the,,
问题出在这一行。
return $argument unless $function = $functiontable{$function};
变量 $function
正在改变用途并从字符串(函数名称)覆盖为代码引用(要执行的函数)。后来,它被传递到 iterate
中,它忠实地忽略了它。
有两件事可以改进此代码并避免此类问题。首先是不要改变变量的用途,使用两个变量。
return $argument unless $function_ref = $functiontable{$function_name};
现在错误不会发生了。您正在重新调整变量用途的一个重要指标是它会更改类型,例如从字符串更改为代码引用。
请注意,我完全放弃了 $function
,因为它在这种情况下太笼统了。那是函数的名称还是函数的引用?两者都不明显,所以要明显。
最后,iterate
可以通过完全删除函数 table 来变得更加灵活。直接传入代码引用。如果你想要一个函数table,写一个包装器。
sub select_iteration {
my($iteration_type, $iterations, $argument) = @_;
my $iteration_code = $iteration_types{$iteration_type};
return iterate($iteration_code, $iterations, $argument);
}
#!/usr/bin/perl
use strict;
use warnings;
use List::MoreUtils 'uniq';
my %functiontable =();
$functiontable{foo} = \&foo;
sub iterate {
my ($function, $iterations, $argument) = @_;
return $argument unless 0 < $iterations;
return $argument unless $function = $functiontable{$function};
my @functioned = $function->($argument);
my @refunctioned = ();
for my $i (0 .. @functioned - 1) {
push @refunctioned, iterate ($function, ($iterations - 1), $functioned[$i]);
}
return uniq @refunctioned;
}
sub foo {
my ($argument) = @_;
my @list = ($argument, $argument.'.', $argument.',');
return @list;
}
my @results = iterate 'foo', 2, 'the';
print "@results";
这会打印 the the. the,
,即它不会迭代(递归)。我希望它打印 the the. the, the.. the., the,. the,,
.
(我用Smart::Comments检查它是否第二次进入iterate
,确实进入了,但它似乎并没有完成函数中的所有事情。)
我不明白为什么。有人可以帮我找出原因,或提出修复建议吗?
这一行:
return $argument unless $function = $functiontable{$function};
没有意义。在您的子例程 iterate
中,$function
是一个字符串,而 $functiontable{$function}
是对子例程的引用。我不确定这样做的目的是什么:是为了与存储的函数进行比较吗?是使用名字$function
引用的函数吗?
假设是后者,在调用迭代时简单地传递对函数的引用会更有意义:
sub iterate {
my ($function, $iterations, $argument) = @_;
return $argument unless 0 < $iterations;
my @functioned = $function->($argument);
my @refunctioned = ();
for my $i (0 .. @functioned - 1) {
push @refunctioned, iterate ($function, ($iterations - 1), $functioned[$i]);
}
return uniq @refunctioned;
}
my @results = iterate($functiontable{foo}, 2, 'the');
print "@results";
输出:
the the. the, the.. the., the,. the,,
第一次调用子例程 iterate
时,它会将 $function
中的子例程名称从名称转换为子例程引用
所以第一次 iterate
调用自身时它正在传递子例程引用,并且行
return $argument unless $function = $functiontable{$function};
将引用字符串化并尝试使用类似于 CODE(0x23e0838)
显然该元素不存在,因此您的 unless
失败并立即返回 $argument
而没有继续递归
更新
我会写这样的东西
#!/usr/bin/perl
use strict;
use warnings;
use 5.10.0;
my %functions = ( foo => \&foo );
sub iterate {
my ($func, $arg, $depth) = @_;
return $arg unless $depth;
map {iterate($func, $_, $depth - 1); } $functions{$func}->($arg);
}
sub foo {
my ($arg) = @_;
map "$arg$_", '', '.', ',';
}
my @results = iterate('foo', 'the', 2);
say "@results";
输出
the the. the, the. the.. the., the, the,. the,,
问题出在这一行。
return $argument unless $function = $functiontable{$function};
变量 $function
正在改变用途并从字符串(函数名称)覆盖为代码引用(要执行的函数)。后来,它被传递到 iterate
中,它忠实地忽略了它。
有两件事可以改进此代码并避免此类问题。首先是不要改变变量的用途,使用两个变量。
return $argument unless $function_ref = $functiontable{$function_name};
现在错误不会发生了。您正在重新调整变量用途的一个重要指标是它会更改类型,例如从字符串更改为代码引用。
请注意,我完全放弃了 $function
,因为它在这种情况下太笼统了。那是函数的名称还是函数的引用?两者都不明显,所以要明显。
最后,iterate
可以通过完全删除函数 table 来变得更加灵活。直接传入代码引用。如果你想要一个函数table,写一个包装器。
sub select_iteration {
my($iteration_type, $iterations, $argument) = @_;
my $iteration_code = $iteration_types{$iteration_type};
return iterate($iteration_code, $iterations, $argument);
}