sub maxNum {
if (@_ == 1) {
return $_[0]; # terminal clause - return immediately
}
my ($first, @rest) = @_;
my $remainingMax = maxNum(@rest);
if ($first > $remainingMax) {
return $first;
}
return $remainingMax;
}
我无法消化使用递归的这段代码。基本上我对my $remainingMax = maxNum(@rest);
部分感到困惑。
我只是想知道当脚本第一次运行时如何找到$remainingMax
的值,之后我明白函数maxNum(@rest)
提供了一个值通过返回ans(即return $first
或return $remainingMax
)。
答案 0 :(得分:5)
递归函数通常遵循“分而治之” - 策略。
查找列表的最大值
max(a, b, c, d)
我们可以任意划分该列表,然后找到所有局部最大值的最大值:
<=> max( max(a, b), max(c, d) )
您的算法选择以下分区:
<=> max( a, max(b, c, d) )
max(b, c, d)
也是如此,导致以下调用图:
max(a, b, c, d)
max(b, c, d)
max(c, d)
max(d)
在max(d)
,算法不会进一步递归。相反,这是基本情况(相当于循环的终止条件)。 max(d)
返回d
。我们现在可以通过将列表其余部分的最大值与第一个值进行比较来找到总的最大值,然后通过调用堆栈返回
这个想法还有许多其他方式可以编码。它可以被翻译成非递归形式
sub maxNum {
my $current_max = pop;
while(@_) {
my $compare = pop;
$current_max = $compare if $compare > $current_max;
}
return $current_max;
}
这会将元素与递归解决方案的顺序进行比较。
查找最大值也可以视为折叠操作(又名reduce
)。我们可以编写一个递归函数来执行以下分区:
max( a, b, c, d )
<=> max( max(a, b), c, d )
<=> max( max(max(a, b), c), d )
这个看起来非常复杂,但却带来了优雅的解决方案:
sub maxNum {
return $_[0] unless $#_; # return if less than two elems
my ($x, $y) = splice 0, 2, @_; # shift first two elems from @_
my $max = $x > $y ? $x : $y; # select larger
return maxNum($max, @_); # recurse
}
立即返回其值的函数调用称为尾调用。我们可以通过使用特殊的goto &subroutine
表达式来提高效率。但是,我们必须手动设置参数列表:
sub maxNum {
return shift unless $#_; # base case: return on one argument
my ($x, $y) = splice 0, 2, @_; # take the first two elems from start
unshift @_, $x > $y ? $x : $y; # put the larger one back there
goto &maxNum; # recurse with tail call
}
答案 1 :(得分:1)
我不明白你的困惑。
第一次maxNum
完成时,它会返回列表的最后一项。
现在,如果你想到最后两个项目的列表,当你传递这些项目时,一个变为$first
,另一个是分配给@rest
的唯一元素。当您将@rest
仅作为一个元素传递时,您将达到终端条件,并返回该元素并将其存储在$remainingMax
中。然后,比较最后两个元素,并返回最大元素。
从那里开始,如果您最初使用大于两个项目的列表调用maxNum
,则考虑返回的最大值,并将其与列表末尾的第三个项目进行比较(Perl下标-3
)。如果这是你的总清单,那么你有最大值。如果没有,则返回该值并将其与最后一个(Perl下标-4)中的第四项进行比较。
准“Perl符号”
maxNum( $_[-1] ) ==> $_[-1];
maxNum( $_[-2..-1] ) ==> $_[-2] > maxNum( $_[-1] ) ? $_[-2] : maxNum( $_[-1] );
maxNum( $_[-3..-1] ) ==> $_[-3] > maxNum( @_[-2..-1] ) ? $_[-3] : maxNum( @_[-2..-1] );
maxNum( $_[-4..-1] ) ==> $_[-4] > maxNum( @_[-3..-1] ) ? $_[-4] : maxNum( @_[-3..-1] );
...