根据定义:
非抢占式递归: 如果一个函数从它的一个形参调用它自己,那么这样的递归称为非抢占式递归。示例:Ackermann函数是一个非抢占式递归的例子
private static int Foo(int i)
{
if (i == 1000000)
return i;
if (i % 100 == 0)
Console.WriteLine(i);
return Foo(Foo(i+1));//last statement of the function
}
这里对Foo
的递归调用是函数的最后一条语句,而不是表达式的一部分,因此它是尾部递归调用。但另一方面,由于Foo
函数作为参数被调用,因此它将导致创建调用堆栈帧,这些帧在终止条件达到之前无法丢弃。
在尾部递归中,编译器可以优化尾部递归调用,因为它不需要维护调用堆栈帧。所以我的问题是-我的递归调用Foo
在上面的代码片段真的是一个尾部递归调用?
非抢占递归算法也可以是尾递归吗?
是的。
在上面的代码片段中,我对Foo
的递归调用真的是一个尾部递归调用吗?
哪一个?有两种递归调用:
-
…(Foo(i+1))…
-非尾递归 -
return Foo(…);
-尾递归
外部的尾部递归可以优化:
private static int Foo(int i) {
while (i != 1000000) {
if (i % 100 == 0) Console.WriteLine(i);
i = Foo(i+1);
}
return i;
}
编译器可能推断出Foo
总是返回1000000
(如果它返回),因此进一步将该方法重写为另一个尾递归,但这需要高级推理,而不是简单的转换:
private static int Foo(int i) {
if (i != 1000000) {
if (i % 100 == 0) Console.WriteLine(i);
} else {
return i;
}
return Foo(i+1);
}
,然后可以通过尾递归规则转换为
private static int Foo(int i) {
while (i != 1000000) {
if (i % 100 == 0) Console.WriteLine(i);
i = i+1;
}
return i;
}