Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jquery/79.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用堆栈重写递归方法?_C# - Fatal编程技术网

C# 如何使用堆栈重写递归方法?

C# 如何使用堆栈重写递归方法?,c#,C#,因为C#不太支持递归调用的优化(例如尾部递归)。似乎我不得不将我的代码风格从函数式编程转换为我不熟悉的东西 我知道有时迭代方法可以替代递归方法,但通常很难找到有效的方法 现在,一般来说,我相信所有递归方法都可以通过某种方式使用stack数据结构来重写 在哪里可以找到这方面的教程、介绍或一般规则 e、 如果我想重写最大公约数法呢?给定m>n int gcd(int m, int n) { if (n==0) return m;

因为C#不太支持递归调用的优化(例如尾部递归)。似乎我不得不将我的代码风格从函数式编程转换为我不熟悉的东西

我知道有时迭代方法可以替代递归方法,但通常很难找到有效的方法

现在,一般来说,我相信所有递归方法都可以通过某种方式使用
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的建议执行!但是使用堆栈给您带来的优化不会那么重要!只有使用尾部递归算法,才能方便地以非递归的方式转换它们,原因是您不需要堆栈来实现这一点!