#!/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
,确实如此,但它似乎没有在函数中执行所有操作。)
我无法弄清楚原因。有人可以帮我弄清楚原因,还是提出修复建议?
答案 0 :(得分:3)
这一行:
return $argument unless $function = $functiontable{$function};
没有意义。在子例程iterate
中,$function
是一个字符串,$functiontable{$function}
是对子例程的引用。我不确定这个的目的是什么:它是否与存储的函数进行比较?它是使用名称$function
引用的函数吗?
假设后者在调用iterate时简单地传入对函数的引用会更有意义:
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,,
答案 1 :(得分:3)
问题在于这一行。
return $argument unless $function = $functiontable{$function};
变量$function
正在被重新利用并从字符串(函数名称)重写为代码引用(要执行的函数)。后来,它被传递到iterate
,忠实地忽略了它。
有两件事会改进这段代码并避免出现这种问题。首先是不重新调整变量,使用两个变量。
return $argument unless $function_ref = $functiontable{$function_name};
现在错误不可能发生。您重新调整变量的一个强有力的指标是它会更改类型,例如从字符串到代码引用。
请注意,我完全抛弃了$function
因为它在这种情况下过于通用。这是函数的名称还是函数的引用?两者都不是显而易见的,所以要明白。
最后,通过完全消除函数表,可以使iterate
更加灵活。直接传入代码引用。如果你想要一个函数表,写一个包装器。
sub select_iteration {
my($iteration_type, $iterations, $argument) = @_;
my $iteration_code = $iteration_types{$iteration_type};
return iterate($iteration_code, $iterations, $argument);
}
答案 2 :(得分:2)
第一次调用子例程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,,