这有资格作为尾递归吗?

时间:2012-09-23 23:04:31

标签: c++ tail-recursion

  

可能重复:
  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);
}

谢谢!

3 个答案:

答案 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

哪个是递归的。