原因我的子程序不会递归

时间:2015-08-17 19:20:41

标签: perl function recursion subroutine perl5

#!/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,确实如此,但它似乎没有在函数中执行所有操作。)

我无法弄清楚原因。有人可以帮我弄清楚原因,还是提出修复建议?

3 个答案:

答案 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,,