如何在同一方法中重新运行方法?

时间:2015-01-20 03:53:35

标签: c#

正如标题所述,如何在同一方法中重新运行方法而不会出现名为StackOverflowException的错误未处理? (我把星号放在问题的地方。“Random rnd = new Random();”然后以绿色突出显示错误。)

private void AI()
    {
        Random rnd = new Random();
        int aimove = rnd.Next(0, 7);
        CheckIfValid();
        if (aimove == 0 && cc0 == false || aimove == 1 && cc1 == false || aimove == 2 && cc2 == false || aimove == 3 && cc3 == false || 
            aimove == 4 && cc4 == false || aimove == 5 && cc5 == false || aimove == 6 && cc6 == false)
        {
            **AI();**
        }
        else if (aimove == 0 && cc0 == true || aimove == 1 && cc1 == true || aimove == 2 && cc2 == true || aimove == 3 && cc3 == true ||
            aimove == 4 && cc4 == true || aimove == 5 && cc5 == true || aimove == 6 && cc6 == true)
        {
            if (aimove == 0)
            {
                c0++;
                Drop(0);
            }
            else if (aimove == 1)
            {
                c1++;
                Drop(1);
            }
            else if (aimove == 2)
            {
                c2++;
                Drop(2);
            }
            else if (aimove == 3)
            {
                c3++;
                Drop(3);
            }
            else if (aimove == 4)
            {
                c4++;
                Drop(4);
            }
            else if (aimove == 5)
            {
                c5++;
                Drop(5);
            }
            else if (aimove == 6)
            {
                c6++;
                Drop(6);
            }
        }
    }

2 个答案:

答案 0 :(得分:1)

如果AI()再次调用自身,那么与aimove值相关的ccX bool目前是假的。

机会是cc1 - cc6都设置为false,因此AI()将始终调用自身,创建Random的新实例,直到出现堆栈溢出。

您需要添加一个退出条件,将cc1-cc6布尔值设置为true,以便递归链结束。

答案 1 :(得分:0)

我将从这里开始:简化代码,以便更容易推理。

我们可以做一些事情。

if语句开始,我们可以这样做:

var ccs = new [] { cc0, cc1, cc2, cc3, cc4, cc5, cc6 };
if (ccs[aimove] == false)
{
    AI();
}

else if条件只是第一个条件的逻辑否定,因此我们只能用else替换。

else中的每个if都做了很多相同的事情。我们可以使用字典来简化它。

所以代码可以成为:

private void AI()
{
    Random rnd = new Random();
    int aimove = rnd.Next(0, 7);
    CheckIfValid();

    var ccs = new [] { cc0, cc1, cc2, cc3, cc4, cc5, cc6 };

    var increments = new Dictionary<int, Action>()
    {
        { 0, () => c0++ },
        { 1, () => c1++ },
        { 2, () => c2++ },
        { 3, () => c3++ },
        { 4, () => c4++ },
        { 5, () => c5++ },
        { 6, () => c6++ },
    };

    if (ccs[aimove] == false)
    {
        AI();
    }
    else
    {
        increments[aimove]();
        Drop(aimove);
    }
}

现在,这仍然有点凌乱。将cc0 .. cc6c0 ... c6重新定义为数组会很高兴,如下所示:

private int[] cs = new int[7];
private bool[] ccs = new bool[7];

然后,您可以像这样重写代码:

private void AI()
{
    Random rnd = new Random();
    int aimove = rnd.Next(0, 7);
    CheckIfValid();
    if (ccs[aimove] == false)
    {
        AI();
    }
    else
    {
        cs[aimove]++;
        Drop(aimove);
    }
}

现在问题可能来自所有ccs值都是假的事实,无论aimove的值是什么,代码都会无限循环。

所以,让我们检查一下。

private void AI()
{
    Random rnd = new Random();
    int aimove = rnd.Next(0, 7);
    CheckIfValid();
    if (ccs.All(cc => cc == false))
    {
        throw new System.InvalidOperationException("All `ccs` are false");
    }
    if (ccs[aimove] == false)
    {
        AI();
    }
    else
    {
        cs[aimove]++;
        Drop(aimove);
    }
}

现在,如果所有ccs都为假,那么异常将触发,您知道您的代码将会破坏堆栈。

另一个问题也会让你感到悲伤,即使某些ccs成立,也可能会导致堆栈崩溃,因为你要为每个问题创建一个新的Random实例致电AI。这个问题是它将在大多数调用中使用相同的种子进行实例化,因为调用是如此迅速。如果它是相同的种子,则对rnd.Next(0, 7)的调用将出现相同的值。如果对AI的每次递归调用都具有相同的aimove值,那么这也可能会快速炸毁堆栈。

诀窍是只创建Random的一个实例,然后重复使用。

所以将代码更改为:

private Random rnd = new Random();

private void AI()
{
    int aimove = rnd.Next(0, 7);
    CheckIfValid();
    if (ccs.All(cc => cc == false))
    {
        throw new System.InvalidOperationException("All `ccs` are false");
    }
    if (ccs[aimove] == false)
    {
        AI();
    }
    else
    {
        cs[aimove]++;
        Drop(aimove);
    }
}

请告诉我这是否可以解决问题。

我怀疑,你真正要做的是选择一个有效的aimove,如果你选择了一个无效的,你正在递归调用AI来选择一个不同的之一。

请改为尝试:

private void AI()
{
    if (ccs.All(cc => cc == false))
    {
        throw new System.InvalidOperationException("All `ccs` are false");
    }
    int aimove = -1;
    do
    {
        aimove = rnd.Next(0, 7);
        CheckIfValid();
    } while (ccs[aimove] == false);
    cs[aimove]++;
    Drop(aimove);
}

这里它只是在AI方法中循环,直到它选择有效的移动。

另一个最终选择是直接使用LINQ选择有效的移动。

private void AI()
{
    var aimove =
        ccs
            .Select((cc, n) => new { cc, n })
            .Where(x => x.cc)
            .OrderBy(x => rnd.Next())
            .Select(x => x.n)
            .First();
    cs[aimove]++;
    Drop(aimove);
}

如果没有任何有效的移动,.First()将抛出异常。我不知道CheckIfValid();正在做什么,所以我不知道把它放在哪里。