C# 如何使用堆栈重写递归方法?
因为C#不太支持递归调用的优化(例如尾部递归)。似乎我不得不将我的代码风格从函数式编程转换为我不熟悉的东西 我知道有时迭代方法可以替代递归方法,但通常很难找到有效的方法 现在,一般来说,我相信所有递归方法都可以通过某种方式使用C# 如何使用堆栈重写递归方法?,c#,C#,因为C#不太支持递归调用的优化(例如尾部递归)。似乎我不得不将我的代码风格从函数式编程转换为我不熟悉的东西 我知道有时迭代方法可以替代递归方法,但通常很难找到有效的方法 现在,一般来说,我相信所有递归方法都可以通过某种方式使用stack数据结构来重写 在哪里可以找到这方面的教程、介绍或一般规则 e、 如果我想重写最大公约数法呢?给定m>n int gcd(int m, int n) { if (n==0) return m;
stack
数据结构来重写
在哪里可以找到这方面的教程、介绍或一般规则
e、 如果我想重写最大公约数法呢?给定m>n
int gcd(int m, int n)
{
if (n==0)
return m;
else
return gcd(n,m%n);
}
更新
可能这是一个糟糕的例子,因为它确实是尾部递归的。请忽略这个事实,认为这是一个普通的递归方法。 在这种情况下,你甚至不需要一个堆栈:
int gcd(int m, int n) {
while(n != 0) {
int aux = m;
m = n;
n = aux % n;
}
return m;
}
一般来说,对于每个尾部递归算法,您不需要堆栈,这是一些编译器可以优化它的方式;但是优化是在不使用调用堆栈的情况下进行的!尾部递归可以通过一个简单的循环进行归档如果我们看一看最简单的情况,那么从这里进行概括应该不会太难 假设我们有一个如下所示的方法:
public void CountToTenInReverse(int curNum)
{
if (curNum >= 11)
return;
CountToTen(curNum + 1);
Console.WriteLine(curNum);
}
让我们看看CountToTenInReverse(1)
的调用堆栈,看看实际发生了什么。打了十个电话后,我们得到了:
[ CountToTenInReverse(10) ] <---- Top of stack
[ CountToTenInReverse(9) ]
[ CountToTenInReverse(8) ]
[ CountToTenInReverse(7) ]
[ CountToTenInReverse(6) ]
[ CountToTenInReverse(5) ]
[ CountToTenInReverse(4) ]
[ CountToTenInReverse(3) ]
[ CountToTenInReverse(2) ]
[ CountToTenInReverse(1) ] <---- Bottom of stack
[CountToTenInReverse(10)]因为示例方法是尾部递归的,所以将其转换为迭代样式很容易,甚至不需要显式堆栈
以下是一些可应用于任何递归函数的步骤:
步骤1:重写该方法,使其自身只调用一次(您的方法已经这样做了),只包含一条return
语句,并使用if
和goto
代替else
、while
、for
和foreach
:
int gcd(int m, int n)
{
int result;
if (n == 0)
{
result = m;
goto done;
}
result = gcd(n, m % n);
done:
return result;
}
步骤2:将递归调用替换为将新参数赋值给参数,并将goto
赋值给方法的开头:
int gcd(int m, int n)
{
int result;
start:
if (n == 0)
{
result = m;
goto done;
}
int old_m = m;
m = n;
n = old_m % n;
goto start;
done:
return result;
}
如果该方法不是尾部递归的,则该方法需要在goto
之前保存其状态,然后再次恢复
步骤3:再次删除转到的s:
int gcd(int m, int n)
{
int result;
while (true)
{
if (n == 0)
{
result = m;
break;
}
int old_m = m;
m = n;
n = old_m % n;
}
return result;
}
步骤4:使方法看起来更漂亮:
int gcd(int m, int n)
{
while (n != 0)
{
int old_m = m;
m = n;
n = old_m % n;
}
return m;
}
下面是一个非尾部递归的示例:
int fac(int x)
{
if (x == 0)
{
return 1;
}
return x * fac(x - 1);
}
第1步:
int fac(int x)
{
int result;
if (x == 0)
{
result = 1;
goto end;
}
result = x * fac(x - 1);
end:
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
int result;
start:
if (x == 0)
{
result = 1;
goto end;
}
stack.Push(x);
x = x - 1;
goto start;
end:
if (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
goto end;
}
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
int result;
while (true)
{
if (x == 0)
{
result = 1;
break;
}
stack.Push(x);
x = x - 1;
}
while (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
}
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
while (x != 0)
{
stack.Push(x);
x = x - 1;
}
int result = 1;
while (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
}
return result;
}
第二步:
int fac(int x)
{
int result;
if (x == 0)
{
result = 1;
goto end;
}
result = x * fac(x - 1);
end:
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
int result;
start:
if (x == 0)
{
result = 1;
goto end;
}
stack.Push(x);
x = x - 1;
goto start;
end:
if (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
goto end;
}
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
int result;
while (true)
{
if (x == 0)
{
result = 1;
break;
}
stack.Push(x);
x = x - 1;
}
while (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
}
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
while (x != 0)
{
stack.Push(x);
x = x - 1;
}
int result = 1;
while (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
}
return result;
}
intfac(intx)
{
堆栈=新堆栈();
int结果;
开始:
如果(x==0)
{
结果=1;
转到终点;
}
堆栈推送(x);
x=x-1;
转到开始;
完:
如果(stack.Count!=0)
{
x=stack.Pop();
结果=x*结果;
转到终点;
}
返回结果;
}
第三步:
int fac(int x)
{
int result;
if (x == 0)
{
result = 1;
goto end;
}
result = x * fac(x - 1);
end:
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
int result;
start:
if (x == 0)
{
result = 1;
goto end;
}
stack.Push(x);
x = x - 1;
goto start;
end:
if (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
goto end;
}
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
int result;
while (true)
{
if (x == 0)
{
result = 1;
break;
}
stack.Push(x);
x = x - 1;
}
while (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
}
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
while (x != 0)
{
stack.Push(x);
x = x - 1;
}
int result = 1;
while (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
}
return result;
}
intfac(intx)
{
堆栈=新堆栈();
int结果;
while(true)
{
如果(x==0)
{
结果=1;
打破
}
堆栈推送(x);
x=x-1;
}
while(stack.Count!=0)
{
x=stack.Pop();
结果=x*结果;
}
返回结果;
}
第4步:
int fac(int x)
{
int result;
if (x == 0)
{
result = 1;
goto end;
}
result = x * fac(x - 1);
end:
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
int result;
start:
if (x == 0)
{
result = 1;
goto end;
}
stack.Push(x);
x = x - 1;
goto start;
end:
if (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
goto end;
}
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
int result;
while (true)
{
if (x == 0)
{
result = 1;
break;
}
stack.Push(x);
x = x - 1;
}
while (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
}
return result;
}
int fac(int x)
{
Stack<int> stack = new Stack<int>();
while (x != 0)
{
stack.Push(x);
x = x - 1;
}
int result = 1;
while (stack.Count != 0)
{
x = stack.Pop();
result = x * result;
}
return result;
}
intfac(intx)
{
堆栈=新堆栈();
而(x!=0)
{
堆栈推送(x);
x=x-1;
}
int结果=1;
while(stack.Count!=0)
{
x=stack.Pop();
结果=x*结果;
}
返回结果;
}
我知道这并不能真正回答您关于如何使用堆栈编程递归调用的问题,但.NET确实支持对尾部调用进行优化。由于JIT编译器的存在以及不同CLR语言编译器之间IL的翻译,它不像编译语言那样简单或直接
也就是说,为什么要担心呢?如果是性能问题,重写该方法并完全消除递归调用。另外,请注意,.NET4.0在这方面做了巨大的改进。您可能担心没有问题。您的gcd示例中没有集合,为什么需要堆栈?你想在那里储存什么?这纯粹是一个例子。。当然,我知道在这种情况下我甚至不需要使用stack
。。但这是我能想到的关于递归的最简单的例子。这纯粹是一个例子。。当然,我知道在这种情况下我甚至不需要使用stack
。。但这是我能想到的关于递归的最简单的例子。我知道如何将递归转换为迭代。我只想学习如何在堆栈中使用尾部递归算法来实现这一点,你不需要堆栈!这就是为什么编译器可以优化它!在非尾部递归算法中,您可以简单地依赖于递归,因为您也将使用堆栈进行方法调用。我要说的是,只有使用尾部递归算法才能进行优化!我相信你说c#编译器不这样做,然后你可以使用简单的无堆栈迭代来模拟尾部递归算法,如示例中所示!对于尾部递归算法,您永远不需要堆栈!在一般情况下,您将按照dlev的建议执行!但是使用堆栈给您带来的优化不会那么重要!只有使用尾部递归算法,才能方便地以非递归的方式转换它们,原因是您不需要堆栈来实现这一点!