什么才是真正的尾递归--各位大侠高手赐教

nasdaq7e7 2007-10-23 08:55:21
最近看数据结构书

发现清华的一本书上讲的尾递归概念不是很明确

便上网搜索下资料,却发现了不同的答案

清华的那本书上讲 如下这个例子就是尾递归
还有C PREMIR PLUS上也是这样的例子
求N!
int fun(n)
{
int f;
if(n==1) f=1;
else f=fun(n-1)*n;
return f;
}

但是网上查到的资料来看 以上2书的并不是真正的尾递归

////////////////////////////////资料如下/////////////////////////
下面用常见的计算n!的两种递归算法分析
线性递归:
long Rescuvie(long n) {
return (n == 1) ? 1 : n * Rescuvie(n - 1);
}

尾递归:
long TailRescuvie(long n, long a) {
return (n == 1) ? a : TailRescuvie(n - 1, a * n);
}

long TailRescuvie(long n) {//封装用的
return (n == 0) ? 1 : TailRescuvie(n, 1);
}

当n = 5时
对于线性递归, 他的递归过程如下
Rescuvie(5)
{5 * Rescuvie(4) }
{5 * {4 * Rescuvie(3) }}
{5 * {4 * {3 * Rescuvie(2) }}}
{5 * {4 * {3 * {2 * Rescuvie(1) }}}}
{5 * {4 * {3 * {2 * 1}}}}
{5 * {4 * {3 * 2}}}
{5 * {4 * 6}}
{5 * 24}
120

对于尾递归, 他的递归过程如下:
TailRescuvie(5)
TailRescuvie(5, 1)
TailRescuvie(4, 5)
TailRescuvie(3, 20)
TailRescuvie(2, 60)
TailRescuvie(1, 120)
120
很容易看出, 普通的线性递归比尾递归更加消耗资源, 在实现上说, 每次重复的过程
调用都使得调用链条不断加长. 系统不得不使用栈进行数据保存和恢复.而尾递归就
不存在这样的问题, 因为他的状态完全由n 和 a 保存.
////////////////////////////////资料结束/////////////////////////

显然上面2书的说法是线性递归而不是尾递归;

资料中的尾递归就是一个线性迭代的过程,
但我们知道函数在调用的时候都要先开辟属于自己的堆栈啊,
尾递归怎么就能例外呢(编译器优化??),
哪位高人给我讲讲,万分感谢!!!
...全文
1795 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
Dobzhansky 2012-10-28
  • 打赏
  • 举报
回复
看看lua里头怎么实现的?
banjiaxi2011 2012-10-28
  • 打赏
  • 举报
回复
尾递归者,伪递归也
lz3771 2009-09-21
  • 打赏
  • 举报
回复
mark
iambic 2009-01-24
  • 打赏
  • 举报
回复
你贴子后面的那个例子对。
不过在C++里搞这个没什么意思。
zenny_chen 2009-01-24
  • 打赏
  • 举报
回复
呵呵,从效率上,如果递归不被优化的话不管是尾递归还是线性递归,都一样。
关键是两者递归的深度都一样,计算量也是一样,所以效率都差不多。
flando 2009-01-24
  • 打赏
  • 举报
回复
A recursive function is said to be tail recursive if all recursive calls within it are tail recursive. A recursive call is tail recursive when it is the last statement that will be executed within the body of a function and its return value is not a part of an expression. Tail-recursive functions are characterized as having nothing to do during the unwinding phase. This characteristic is important because most modern compilers automatically generate code to take advantage of it.
/*****************************************************************************
* *
* ------------------------------ facttail.c ------------------------------ *
* *
*****************************************************************************/

#include "facttail.h"

/*****************************************************************************
* *
* ------------------------------- facttail ------------------------------- *
* *
*****************************************************************************/

int facttail(int n, int a) {

/*****************************************************************************
* *
* Compute a factorial in a tail-recursive manner. *
* *
*****************************************************************************/

if (n < 0)
return 0;
else if (n == 0)
return 1;
else if (n == 1)
return a;
else
return facttail(n - 1, n * a);

}

gongyiling 2007-11-09
  • 打赏
  • 举报
回复
其实VC6是支持尾递归优化的,尾递归优化对编译器来说是很简单的事,楼主的汇编代码之所以没有得到优化后的结果,是因为生成的程序是Debug设置的,看看这个程序:
int sum(int seed,int start,int end)
{

if(start<=end)
return sum(seed+start,start+1,end);
else
return seed;
}
程序是求一从start到end之间的数之和,经过优化编译后,反汇编得到的结果为:
00401000 >/$ 8B4C24 08 mov ecx, dword ptr [esp+8] ; 源文件 F:\vc_programme\tail\tail.cpp
00401004 |. 8B5424 0C mov edx, dword ptr [esp+C]
00401008 |. 8B4424 04 mov eax, dword ptr [esp+4]
0040100C |. 3BCA cmp ecx, edx
0040100E |. 7F 07 jg short 00401017
00401010 |> 03C1 /add eax, ecx
00401012 |. 41 |inc ecx
00401013 |. 3BCA |cmp ecx, edx
00401015 |.^ 7E F9 \jle short 00401010
00401017 \> C3 retn
可见VC6的优化效率!!
nasdaq7e7 2007-10-25
  • 打赏
  • 举报
回复
呵呵,请问怎么给分来的
请问点管理帖子后 分配好分了 然后点什么呢?
zhangyanli 2007-10-24
  • 打赏
  • 举报
回复
楼主给分啊
zhangyanli 2007-10-24
  • 打赏
  • 举报
回复
vc6不支持尾递归优化的
nasdaq7e7 2007-10-24
  • 打赏
  • 举报
回复
再次顶起 盼回答哦
nasdaq7e7 2007-10-23
  • 打赏
  • 举报
回复
请问各位是不是 VC6.0不支持尾递归的优化呢?
帮忙看下这段汇编代码
引用
6: long Rescuvie(long n)
7: {
00401030 push ebp
00401031 mov ebp,esp
00401033 sub esp,40h
00401036 push ebx
00401037 push esi
00401038 push edi
00401039 lea edi,[ebp-40h]
0040103C mov ecx,10h
00401041 mov eax,0CCCCCCCCh
00401046 rep stos dword ptr [edi]
8: if(n==1) return 1;
00401048 cmp dword ptr [ebp+8],1
0040104C jne Rescuvie+25h (00401055)
0040104E mov eax,1
00401053 jmp Rescuvie+3Ah (0040106a)
9: else return n*Rescuvie(n-1);
00401055 mov eax,dword ptr [ebp+8]
00401058 sub eax,1
0040105B push eax
0040105C call @ILT+10(Rescuvie) (0040100f)
00401061 add esp,4
00401064 mov ecx,dword ptr [ebp+8]
00401067 imul eax,ecx
10: }
0040106A pop edi
0040106B pop esi
0040106C pop ebx
0040106D add esp,40h
00401070 cmp ebp,esp
00401072 call __chkesp (00401160)
00401077 mov esp,ebp
00401079 pop ebp
0040107A ret
以上是正常递归

以下是尾递归
12: long TailRescuvie(long n,long a)
13: {
00401090 push ebp
00401091 mov ebp,esp
00401093 sub esp,40h
00401096 push ebx
00401097 push esi
00401098 push edi
00401099 lea edi,[ebp-40h]
0040109C mov ecx,10h
004010A1 mov eax,0CCCCCCCCh
004010A6 rep stos dword ptr [edi]
14: if(n==1) return a;
004010A8 cmp dword ptr [ebp+8],1
004010AC jne TailRescuvie+23h (004010b3)
004010AE mov eax,dword ptr [ebp+0Ch]
004010B1 jmp TailRescuvie+3Ah (004010ca)
15: else return TailRescuvie(n-1,a*n);
004010B3 mov eax,dword ptr [ebp+0Ch]
004010B6 imul eax,dword ptr [ebp+8]
004010BA push eax
004010BB mov ecx,dword ptr [ebp+8]
004010BE sub ecx,1
004010C1 push ecx
004010C2 call @ILT+0(TailRescuvie) (00401005)
004010C7 add esp,8
16: }
004010CA pop edi
004010CB pop esi
004010CC pop ebx
004010CD add esp,40h
004010D0 cmp ebp,esp
004010D2 call __chkesp (00401160)
004010D7 mov esp,ebp
004010D9 pop ebp
004010DA ret


各位帮忙看看 是否这是优化的尾递归
按理说 优化后的函数调用不应该用CALL 而应该是JMP了吧
nasdaq7e7 2007-10-23
  • 打赏
  • 举报
回复
谢谢各位 尤其是zhangyanli 这位朋友

zhangyanli 2007-10-23
  • 打赏
  • 举报
回复
另外:
"但我们知道函数在调用的时候都要先开辟属于自己的堆栈啊, 尾递归怎么就能例外呢(编译器优化??),"
尾递归不例外,照样有自己的堆栈,可能是编译器的优化,把call指令变成loop指令(呵呵,猜的).但无论如何
你的说法是不正确的.
zhangyanli 2007-10-23
  • 打赏
  • 举报
回复
楼主:
"系统不得不使用栈进行数据保存和恢复.而尾递归就 不存在这样的问题, 因为他的状态完全由n 和 a 保存. "
任何函数都需要用栈保存和恢复数据,尾递归照样需要啊.他需要传入参数(压栈)和调用return 然后出栈恢复数据.
尾递归在上题中是因为写法不同而不用保存那些临时的1 2 3 4 5,但是他照样是函数调用,没有所谓的"不存在这样的问题,"
只是编译器可以做不同的优化罢了,我也找资料了,本质是编译器的优化导致的效率差异
WinWing 2007-10-23
  • 打赏
  • 举报
回复
尾递归
iwillalwaysloveyou 2007-10-23
  • 打赏
  • 举报
回复
这算什么递归?


class Recursive
{
private:
long result;
void CalcResult(long n)
{
if(n > 1)
{
result *= n;
CalcResult(n - 1);
}
}

public:
long GetResult(long n)
{
result = 1;
CalcResult(n);
return result;
}
}
zhongguoren666 2007-10-23
  • 打赏
  • 举报
回复
支持楼上,楼上说的很对.
xiantongyuan 2007-10-23
  • 打赏
  • 举报
回复
尾递归就是从最后开始计算,每递归一次就算出相应的结果,
而线形递归不算,直到递归到一个确定的值后,又从这个具体值向后计算。所以线形递归肯定费事。

64,639

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧