可能重复:
Tail recursion in C++
我是c ++中尾部递归的新手。我的项目要求我使所有函数尾递归。我已经测试了以下代码,它可以正常工作。但是,我不确定我是如何完成它的,因为它有资格作为尾递归。
static int sum_helper(list_t hList, int accumulator){
if (list_isEmpty(hList))
return accumulator;
else {
accumulator += list_first(hList);
hList = list_rest(hList);
return sum_helper(hList, accumulator);
}
}
int sum(list_t list){
/*
// EFFECTS: returns the sum of each element in list
// zero if the list is empty.
*/
if (list_isEmpty(list))
return 0;
return sum_helper(list, 0);
}
谢谢!
答案 0 :(得分:2)
简而言之,在递归调用(sum_helper
)之后,您不会执行任何操作。这意味着您永远不需要返回调用者,因此,您可以丢弃调用者的堆栈帧。
以正常因子函数为例
int fact(int x)
{
if(x == 0)
return 1;
else
return x * fact(x-1);
}
这不是尾递归,因为需要返回fact(x-1)
的值,然后再乘以6。相反,我们可以作弊,并通过累加器。见:
int fact(int x, int acc)
{
if(x == 0)
return acc; // Technically, acc * 1, but that's the identity anyway.
else
return fact(x-1, acc*x);
}
这里,控制流程中的最后一个函数调用是fact(x-1, acc*x)
。之后,我们不需要将任何被调用函数的返回值用于其他任何东西,因此我们不需要需要来返回当前帧。出于这个原因,我们可以丢弃堆栈帧并应用其他优化。
免责声明:我可能错误地应用了因子算法,但是你得到了这个jist。希望。
答案 1 :(得分:2)
这是尾递归,前提是list_t没有非平凡的析构函数。如果它确实有一个非平凡的析构函数,析构函数需要在递归调用返回之后和函数本身返回之前运行。
加成:
int sum(list_t hList, int accumulator = 0) {
return list_isEmpty(hList)
? 0
: sum(list_rest(hList), accumulator + list_first(hList));
}
但口味各不相同;有些人可能会更喜欢你的。
答案 2 :(得分:1)
从理论的角度来看,是的,它是尾递归(假设hList没有非特定的析构函数)。但从实际的角度来看,它取决于您的编译器及其设置。我们来看看为这个简单代码生成的程序集:
#include <cstdlib>
struct list{
int head;
list * tail;
};
int sum_helper(list * l, int accumulator){
if (l == NULL)
return accumulator;
else {
accumulator += l->head;
return sum_helper(l->tail, accumulator);
}
}
优化ON :( g ++ -O2 ...,省略无聊部分):
testq %rdi, %rdi
movl %esi, %eax
je .L2
...
.L6:
...
jne .L6 <-- loop
.L2:
rep
ret
这显然是一个循环。但是当你禁用优化时,你得到:
_Z10sum_helperP4listi:
.LFB6:
...
jne .L2
movl -12(%rbp), %eax
jmp .L3
.L2:
...
call _Z10sum_helperP4listi <-- recursion
.L3:
leave
.cfi_def_cfa 7, 8
ret
哪个是递归的。