总是要避免尾递归函数吗?

时间:2010-07-20 11:30:17

标签: performance recursion tail-recursion

如果我没记错的话,尾递归函数总是有一个简单的非递归等价。 由于递归涉及不必要的函数调用开销,因此最好以非递归方式进行。

这种假设总是如此吗?对于尾递归是否还有其他参数?

4 个答案:

答案 0 :(得分:6)

如果您正在使用具有良好编译器的语言,那么可以优化这些类型的递归,因此在这些情况下如果它提高了使用递归的可读性,我会坚持使用它。

答案 1 :(得分:6)

不,并非总是如此。许多语言和/或编译器可以轻松地优化尾递归调用,并将其重写为迭代版本,或者以某种方式重用堆栈帧以用于后续调用。

Scheme语言要求实施使用tail call optimization

gcc也可以优化尾调用,考虑一个释放链表中所有节点的函数:

void free_all(struct node *n)
{
    if(n != NULL) {
        struct node *next = n->next;
        free(n);
        free_all(next);
    }
}

通过优化编译:

free_all:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ebx
        subl    $20, %esp
        movl    8(%ebp), %eax
        testl   %eax, %eax
        je      .L4
        .p2align 4,,7
        .p2align 3
.L5:
        movl    4(%eax), %ebx
        movl    %eax, (%esp)
        call    free
        testl   %ebx, %ebx
        movl    %ebx, %eax
        jne     .L5
.L4:
        addl    $20, %esp
        popl    %ebx
        popl    %ebp
        ret

即,简单的跳转而不是递归调用free_all

答案 2 :(得分:2)

没有

寻求可读性。许多计算更好地表示为递归(尾部或其他)函数。避免它们的另一个原因是,如果你的编译器没有进行尾调用优化,并且你希望你可能会破坏调用堆栈。

答案 3 :(得分:1)

这取决于语言,但通常开销并不大。它可能是主观的,但递归函数往往更容易理解。大多数时候你都不会注意到性能差异。

除非我的平台在处理它时非常糟糕(即根本不做,但总是推到堆栈上),我会去尾递归。