我正在使用Perl来提取bitbucket repo列表。来自bitbucket的响应将只包含10个存储库和下一页的标记,其中将有另外10个存储库等等...(它们称之为分页响应)
所以,我编写了一个递归子程序,如果存在下一个页面标记,它会调用自身。这将一直持续到最后一页。
这是我的代码:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
use LWP::UserAgent;
use JSON;
my @array;
recursive("my_bitbucket_url");
foreach ( @array ) { print $_."\n"; }
sub recursive
{
my $url = $_[0];
### here goes my LWP::UserAgent code which connects to bitbucket and pulls back the response in a JSON as $response->decoded_content
### hence, removing this code for brevity
my $hash = decode_json $response->decoded_content;
#print Dumper ($hash);
foreach my $a ( @{$hash->{values}} )
{
push @array, $a->{links}->{self}->{href};
}
if ( defined $hash->{next})
{
print "Next page Exists \n";
print "Recursing with $hash->{next} \n";
recursive( $hash->{next} );
}
else
{
print "Last page reached. No more recursion \n"
}
}
现在,我的代码工作正常,它列出了所有的回购。
问题:
我不确定我使用上面的变量 my @array;
的方式。我已经在子例程之外定义了它,但是,我直接从子例程访问它。不知何故,我觉得这不对。
那么,在这种情况下如何使用递归子例程附加到数组。我的代码是否遵循Perl伦理,或者它是否真的荒谬(但是正确,因为它有效)?
更新
根据@ikegami,@ Sobrique和@Hynek -Pichi- Vychodil的建议,我得到了下面的代码,它使用了while
循环并避免了recusrsion。
这是我的思考过程:
@array
。call_url
,并将响应保存在$hash
$hash
以查看 next 页面标记
@array
并使用新标记调用call_url
。这将通过while
循环完成。@array
。周期。@array
内容。这是我的代码:
my @array;
my $hash = call_url("my_bitbucket_url ");
if (defined $hash->{next})
{
while (defined $hash->{next})
{
foreach my $a ( @{$hash->{values}} )
{
push @array, $a->{links}->{self}->{href};
}
$hash = call_url($hash->{next});
}
}
foreach my $a ( @{$hash->{values}} )
{
push @array, $a->{links}->{self}->{href};
}
foreach (@array) { print $_."\n"; }
sub call_url
{
### here goes my LWP::UserAgent code which connects to bitbucket and pulls back the response in a JSON as $response->decoded_content
### hence, removing this code for brevity
my $hash = decode_json $response->decoded_content;
#print Dumper ($hash);
return $hash;
}
肯定想知道这看起来不错还是还有改进的余地。
答案 0 :(得分:3)
使用全局变量返回值演示high coupling,这是值得避免的。
您问以下是否可以接受:
my $sum;
sum(4, 5);
print("$sum\n");
sub sum {
my ($x, $y) = @_;
$sum = $x + $y;
}
sub是递归的事实完全无关紧要;它只是让你的榜样更大。
问题已解决:
sub recursive
{
my $url = $_[0];
my @array;
my $hash = ...;
foreach my $a ( @{$hash->{values}} )
{
push @array, $a->{links}->{self}->{href};
}
if ( defined $hash->{next})
{
print "Next page Exists \n";
print "Recursing with $hash->{next} \n";
push @array, recursive( $hash->{next} );
}
else
{
print "Last page reached. No more recursion \n"
}
return @array;
}
{
my @array = recursive("my_bitbucket_url");
foreach ( @array ) { print $_."\n"; }
}
删除了递归:
sub recursive
{
my $url = $_[0];
my @array;
while (defined($url)) {
my $hash = ...;
foreach my $a ( @{$hash->{values}} )
{
push @array, $a->{links}->{self}->{href};
}
$url = $hash->{next};
if ( defined $url)
{
print "Next page Exists \n";
print "Recursing with $url\n";
}
else
{
print "Last page reached. No more recursion \n"
}
}
return @array;
}
{
my @array = recursive("my_bitbucket_url");
foreach ( @array ) { print $_."\n"; }
}
清理您发布的最新代码:
my $url = "my_bitbucket_url";
my @array;
while ($url) {
my $hash = call_url($url);
for my $value ( @{ $hash->{values} } ) {
push @array, $value->{links}{self}{href};
}
$url = $hash->{next};
}
print("$_\n") for @array;
答案 1 :(得分:2)
是的,使用全局变量是一个坏习惯,即使它是词法范围变量。
每个递归代码都可以重写为命令式循环版本,反之亦然。这是因为所有这些都是在CPU上实现的,根本不知道有关递归的任何信息。只是跳跃。所有的调用和返回只是通过一些堆栈操作跳转,因此您可以将递归算法重写为循环。如果它不是那么明显和简单,在这种情况下,您甚至可以模拟堆栈和行为,就像在您喜欢的语言解释器或编译器中完成一样。在这种情况下,它非常简单:
my @array = with_loop("my_bitbucket_url");
foreach ( @array ) { print $_."\n"; }
sub with_loop
{
my $url = $_[0];
my @array;
while(1)
{
### here goes my LWP::UserAgent code which connects to bitbucket and
### pulls back the response in a JSON as $response->decoded_content
### hence, removing this code for brevity
my $hash = decode_json $response->decoded_content;
#print Dumper ($hash);
foreach my $a ( @{$hash->{values}} )
{
push @array, $a->{links}->{self}->{href};
}
unless ( defined $hash->{next})
{
print "Last page reached. No more recursion \n";
last
};
print "Next page Exists \n";
print "Recursing with $hash->{next} \n";
$url = $hash->{next};
};
return @array;
}
但是当你想坚持递归时,你可以,但它有点棘手。首先,没有尾部调用优化,因此您不必像原始版本那样尝试编写尾部调用代码。所以你可以这样做:
my @array = recursion("my_bitbucket_url");
foreach ( @array ) { print $_."\n"; }
sub recursion
{
my $url = $_[0];
### here goes my LWP::UserAgent code which connects to bitbucket and
### pulls back the response in a JSON as $response->decoded_content
### hence, removing this code for brevity
my $hash = decode_json $response->decoded_content;
#print Dumper ($hash);
# this map version is same as foreach with push but more perlish
my @array = map $_->{links}->{self}->{href}, @{$hash->{values}};
if (defined $hash->{next})
{
print "Next page Exists \n";
print "Recursing with $hash->{next} \n";
push @array, recursive( $hash->{next} );
}
else
{
print "Last page reached. No more recursion \n"
}
return @array;
}
但是这个版本效率不高所以有办法在perl中编写尾调用递归版本,这有点棘手。
my @array = tail_recursive("my_bitbucket_url");
foreach ( @array ) { print $_."\n"; }
sub tail_recursive
{
my $url = $_[0];
my @array;
return tail_recursive_inner($url, \@array);
# url is mutable parameter
}
sub tail_recursive_inner
{
my $url = $_[0];
my $array = $_[1];
# $array is reference to accumulator @array
# from tail_recursive function
### here goes my LWP::UserAgent code which connects to bitbucket and
### pulls back the response in a JSON as $response->decoded_content
### hence, removing this code for brevity
my $hash = decode_json $response->decoded_content;
#print Dumper ($hash);
foreach my $a ( @{$hash->{values}} )
{
push @$array, $a->{links}->{self}->{href};
}
if (defined $hash->{next})
{
print "Next page Exists \n";
print "Recursing with $hash->{next} \n";
# first parameter is mutable so its OK to assign
$_[0] = $hash->{next};
goto &tail_recursive_inner;
}
else
{
print "Last page reached. No more recursion \n"
}
return @$array;
}
如果你对一些真正的perl技巧感兴趣
print $_."\n" for tricky_tail_recursion("my_bitbucket_url");
sub tricky_tail_recursion {
my $url = shift;
### here goes my LWP::UserAgent code which connects to bitbucket and
### pulls back the response in a JSON as $response->decoded_content
### hence, removing this code for brevity
my $hash = decode_json $response->decoded_content;
#print Dumper ($hash);
push @_, $_->{links}->{self}->{href} for @{$hash->{values}};
if (defined $hash->{next}) {
print "Next page Exists \n";
print "Recursing with $hash->{next} \n";
unshift @_, $hash->{next};
goto &tricky_tail_recursion;
} else {
print "Last page reached. No more recursion \n"
};
return @_;
}
另请参阅:LWP::UserAgent
docs。
答案 2 :(得分:1)
整个程序可以使用在任何闭包之外定义的变量。它工作正常,没有什么可担心的。有些人可能称之为“糟糕的风格”。在某些情况下(主要是围绕节目长度和距离动作),但这不是一个严格的约束。
我不确定我是否一定会看到这里递归的优势 - 你的问题似乎似乎无法保证。这本身并不是问题,但对于未来的维护程序员来说可能有点混乱;)。
我正在思考(非递归)的事情:
my $url = "my_bitbucket_url";
while ( defined $url ) {
##LWP Stuff;
my $hash = decode_json $response->decoded_content;
foreach my $element ( @{ $hash->{values} } ) {
print join( "\n", @{ $element->{links}->{self}->{href} } ), "\n";
}
$url = $hash->{next}; #undef if it doesn't exist, so loop breaks.
}