Я не могу понять, что вы хотите 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
захватывает ссылку на себя, она никогда не будет очищена (до завершения программы). Теперь я использую функцию weaken
из Scalar :: Util (как упомянул singingfish в его записи в блоге ), чтобы избежать этого. $strongRef
используется только для того, чтобы предотвратить сбор мусора до того, как мы закончили с этим.