【问题标题】:Reason my subroutine won't recurse原因我的子程序不会递归
【发布时间】:2015-08-17 19:20:41
【问题描述】:
#!/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,它确实输入了,但它似乎并没有完成函数中的所有操作。)

我不知道为什么。有人可以帮我找出原因或提出解决方案吗?

【问题讨论】:

  • 您将变量$function 从名称修改为子例程引用,然后将引用传递给迭代方法。您可能想再次传递该名称。
  • @Miller,非常感谢。
  • 共有三个答案,每个答案都是正确的 AFAICT,每个答案都更强烈地触及到其他人掩盖的一点(Hunter McMillen's 直接使用 ref 作为参数,Borodin's关于究竟是什么导致我的版本失败,Schwern's 关于智能变量的使用)。我不知道该接受哪一个:所有人都非常有帮助。
  • @JQKP perl -wle '@choices = qw(Schwern Borodin Hunter); print $choices[rand @choices]'

标签: function recursion subroutine perl


【解决方案1】:

这一行:

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

【讨论】:

  • 请注意 = 不是 ==。我是在分配而不是比较。但是非常感谢(其余的)答案。
  • @JQKP 啊我的错,以为是错字。
【解决方案2】:

问题是这一行。

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

【讨论】:

    【解决方案3】:

    第一次调用您的子例程 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,,
    

    【讨论】:

    • 非常感谢。 (请注意,这与 Miller 之前对该问题的评论相符。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-12
    • 2017-11-12
    相关资源
    最近更新 更多