4.1.2 递归函数
鉴于上面提到了递归并给出了一个相当奇特的例子(其中两个过程互相调用),那么我再向您展示一个递归函数调用自身的经典示例。使用递归通常是编写循环的一种替代方法。
举个经典的例子,假设你想计算一个数字的幂,但你没有合适的函数(当然,运行时库中有相应的函数)。你可能还记得数学中 2 乘以 3 的幂相当于 2 乘以自身 3 倍,即 2*2*2。
用代码去实现这个算法的一种方式是编写一个for循环,该循环执行3次(或指数的值),并从1开始将2(或基数的值)乘以当前总数:
function PowerL(Base, Exp: Integer): Integer;
var
I: Integer;
begin
Result := 1;
for I := 1 to Exp do
Result := Result * Base;
end;
另一种方法是反复将基数乘以相同数字的幂,指数递减直为0,此时结果无疑为1。可以通过递归方式一遍又一遍地调用相同的函数来实现:
function PowerR(Base, Exp: Integer): Integer;
var
I: Integer;
begin
if Exp = 0 then
Result := 1
else
Result := Base * PowerR(Base, Exp - 1);
end;
程序的递归版本可能不会比基于 for 循环的版本更快,也不会更具可读性。不过,在某些情况下,例如解析代码结构(如树结构)时,需要处理的元素数量并不固定,因此编写循环几乎是不可能的,而递归函数却能适应这种情况。
一般来说,递归代码功能强大,但往往更为复杂。与编程早期相比,递归几乎被遗忘了很多年,但 Haskell、Erlang 和 Elixir 等新型函数式语言大量使用了递归,并使这一思想重新流行起来。
无论如何,你都可以在 FunctionTest
示例的代码中找到这两个幂函数。
注解:演示中的两个幂函数不处理负指数情况。在这种情况下,递归版本将永远循环(或者更准确地说,直到程序遇到物理限制)。此外,通过使用整数,达到最大数据类型大小并溢出它的速度相对较快。我在编写这些函数时考虑到了这些固有的限制,以尽量保持代码的简洁。