【问题标题】:Encapsulating Recursive Function in Perl在 Perl 中封装递归函数
【发布时间】:2010-11-01 06:19:51
【问题描述】:

我有一个在这里运行良好的代码:

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;



        my %graph =(
            F => ['B','C','E'],
            A => ['B','C'],
            D => ['B'],
            C => ['A','E','F'],
            E => ['C','F'],
            B => ['A','E','F']
        );

        sub findPaths {
            my( $seen,  $start, $end ) = @_;

            return [[$end]] if $start eq $end;

            $seen->{ $start } = 1;
            my @paths;
            for my $node ( @{ $graph{ $start } } ) {
                my %seen = %{$seen};
                next if exists $seen{ $node };
                push @paths, [ $start, @$_ ] for @{ findPaths( \%seen, $node, $end ) };
            }
            return \@paths;
        }


            my $start = "B";
            my $end   = "E";
        print "@$_\n" for @{ findPaths( {}, $start, $end ) };

我想做的是生成一个更通用的子程序 这样它只需要\%graph, $start,$end 作为输入并返回最终数组。

我尝试过这种方式,但它无法编译。

sub findPathsAll {

    my ($graph,$start,$end) = @_;

    my $findPaths_sub;
        $findPaths_sub {
            my( $seen) = @_;

            return [[$end]] if $start eq $end;

            $seen->{ $start } = 1;
            my @paths;
            for my $node ( @{ $graph{ $start } } ) {
                my %seen = %{$seen};
                next if exists $seen{ $node };
                push @paths, [ $start, @$_ ] for @{ &$findPaths_sub( \%seen, $node, $end ) };
            }
            return \@paths;
        }


    my @all;
    push @all,@$_ for @{ &$findPaths_sub( {}, $start, $end ) };
    return @all;
}

正确的做法是什么?

【问题讨论】:

    标签: perl function subroutine


    【解决方案1】:

    我不知道你想要findPathsAll 返回什么,所以这可能不是你想要的。无论如何,这里将findPaths 翻译成词法递归代码引用:

    use Scalar::Util 'weaken';
    
    sub findPathsAll {
      my ($graph,$start,$end) = @_;
    
      my $findPaths_sub;
      my $strongRef = $findPaths_sub = sub {
        my( $seen, $start, $end ) = @_;
    
        return [[$end]] if $start eq $end;
    
        $seen->{ $start } = 1;
        my @paths;
        for my $node ( @{ $graph->{ $start } } ) {
          my %seen = %{$seen};
          next if exists $seen{ $node };
          push @paths, [ $start, @$_ ]
              for @{ $findPaths_sub->( \%seen, $node, $end ) };
        }
        return \@paths;
      };
    
      weaken($findPaths_sub);       # Prevent memory leak
    
      my @all;
      push @all,@$_ for @{ $findPaths_sub->( {}, $start, $end ) };
      return @all;
    
      ## The above turns all the paths into one big list of nodes.
      ## I think maybe you meant this:
      #    return @{ $findPaths_sub->( {}, $start, $end ) };
      ## which would return a list of arrayrefs, one for each path.
    }
    

    一些注意事项:

    你这样声明一个coderef:

    $var = sub { ... };
    

    注意赋值运算符和结尾的分号。如果您希望 coderef 是递归的,您必须已经声明了$var。 (如果你说my $var = sub { ... };,新变量直到子创建之后才存在,所以它不能引用$var。)

    你这样称呼它:

    $var->(arg1, arg2, ...);
    

    (还有其他可用的语法,但我认为这是首选。)

    在我发布的第一个版本中存在细微的内存泄漏。 Perl 使用引用计数垃圾收集器,这意味着它不能删除自引用数据结构。由于$findPaths_sub 中的coderef 捕获了对自身的引用,它永远不会被清理(直到程序退出)。我现在使用the weaken function from Scalar::Util(正如singingfish 在his blog entry 中提到的那样)来避免这种情况。 $strongRef 仅用于防止 coderef 在我们完成之前被垃圾收集。

    【讨论】:

      【解决方案2】:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-01
        • 1970-01-01
        • 2016-07-12
        • 2020-04-09
        • 2022-01-01
        • 1970-01-01
        • 2012-11-26
        相关资源
        最近更新 更多