在 Perl 中封装递归函数
我有一个在这里运行良好的代码:
#!/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;
}
正确的做法是什么?
I have a code that works well here:
#!/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 ) };
What I want to do is to generate a more general subroutine
so that it just take \%graph, $start,$end
as input and return final array.
I tried to do it this way but it doesn't compile.
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;
}
What's the right way to do it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我在博客上讨论了这个问题 前几天。
I blogged about this very issue the other day.
我无法弄清楚您想要
findPathsAll
返回什么,所以这可能不是您想要的。无论如何,这里是将findPaths
翻译成词法递归 coderef:一些注意事项:
您可以像这样声明 coderef:
注意赋值运算符和尾随分号。如果您希望 coderef 是递归的,则必须已经声明
$var
。 (如果您说my $var = sub { ... };
,则新变量直到创建子之后才存在,因此它无法引用到$var
。)您可以这样称呼它:(
还有其他有效的语法,但我认为这是首选语法。)
我发布的第一个版本中存在微妙的内存泄漏。 Perl 使用引用计数垃圾收集器,这意味着它无法删除自引用数据结构。由于 $findPaths_sub 中的 coderef 捕获了对其自身的引用,因此它永远不会被清除(直到程序退出)。我现在使用 Scalar::Util 中的 weaken 函数< /a> (如 singfish 在 中提到的他的博客条目)以避免这种情况。
$strongRef
仅用于防止 coderef 在我们使用完毕之前被垃圾回收。I can't figure out what you want
findPathsAll
to return, so this may not be quite what you want. Anyway, here's a translation offindPaths
into a lexical recursive coderef:Some notes:
You declare a coderef like this:
Note the assignment operator and the trailing semicolon. If you want the coderef to be recursive, you must have already declared
$var
. (If you saymy $var = sub { ... };
, the new variable doesn't exist until after the sub is created, so it can't refer to$var
.)You call it like this:
(There are other syntaxes that work, but I think this is the preferred one.)
There was a subtle memory leak in the first version I posted. Perl uses a reference-count garbage collector, which means it can't delete self-referential data structures. Since the coderef in
$findPaths_sub
captured a reference to itself, it would never be cleaned up (until program exit). I now use theweaken
function from Scalar::Util (as mentioned by singingfish in his blog entry) to avoid that.$strongRef
is used only to keep the coderef from being garbage collected before we're done with it.