C#递归收益返回不返回任何内容

时间:2020-06-16 05:02:28

标签: c# algorithm recursion permutation yield

可能已经有人问过这个问题,但是我找不到解决问题的方法。问题不是特定于语言的,可以在python中询问相同的问题。该任务是一种算法,用于生成诸如Enumerable.Range之类的字符串列表,但字符不仅限于1,2,3 ...,而且可以是任何字符序列。最简单的示例是:

TestCase 1
输入:

baseChars:['a','b'],
所需的字符串长度:2

输出:

['aa','ab','ba','bb']

TestCase 2

baseChars:['a','b']
所需的字符串长度:1

输出:

['a','b']

该功能运行良好:

    static IList<string> baseChars = new List<string>() { "0", "1", "2", "3" };
    static void CharsRange1(string prefix, int pos)
    {
        if (pos == 1)
        {
            foreach (string s in baseChars)
            {
                Console.WriteLine(prefix + s);
            }
        }
        else
        {
            foreach (string s in baseChars)
            {
                CharsRange1(prefix + s, pos - 1);
            }
        }
    }

预期和实际输出(用逗号替换换行符以节省空间)

000,001,002,003,010,011,012,013,020,021,022,023,030,031, 032、033、100、101、102、103、110、111、112、113、120、121、122、123, 130、131、132、133、200、201、202、203、210、211、212、213、220、221, 222、223、230、231、232、233、300、301、302、303、310、311、312、313, 320、321、322、323、330、331、332、333

问题是将此函数封装为一个库,因此返回类型应为IEnumerable<string>,因此即使输入较大的内存也不会爆炸。但我的代码无法返回任何内容

    static IEnumerable<string> CharsRange2(string prefix, int pos)
    {
        if (pos == 1)
        {
            foreach (string s in baseChars)
            {
                yield return prefix + s;
            }
        }
        else
        {
            foreach (string s in baseChars)
            {
                // here if i yield return then won't compile
                // i thought at the end of recursive loop it will return 
                CharsRange2(prefix + s, pos - 1);
            }
        }
    }

主要:

    static void Main(string[] args)
    {
        //CharsRange1("", 3);//working
        foreach (string s in CharsRange2("", 3))
        {
            Console.WriteLine(s);//nothing
        }
        Console.WriteLine("end");
        Console.ReadKey();
    }

有人可以帮忙吗?我已将代码放入github中。如果您可以将我的实现更改为非递归但保留函数的返回类型,也将不胜感激。

2 个答案:

答案 0 :(得分:4)

选项1,从递归调用中产生每个值。

    foreach (string s in baseChars)
        foreach (var r in CharsRange2(prefix + s, pos - 1))
            yield return r;

选项2,重复使用框架中内置的现有IEnumerable类型,以完全避免收益率的产生;

    if (pos == 1)
        return baseChars.Select(s => prefix + s);
    else
        return baseChars.SelectMany(s => CharsRange2(prefix + s, pos - 1));

选项3,使用嵌套循环代替递归方法,留给读者练习。

答案 1 :(得分:2)

如前所述,未使用呼叫CharsRange2(prefix + s, pos - 1);。您需要嵌套foreachyield每个结果。

这是一种更多基于Enumerable.Range的想法。

从通用基础转换器开始:

public static IEnumerable<int> ToBase(this int x, int b)
{
    IEnumerable<int> ToBaseReverse()
    {
        if (x == 0)
        {
            yield return 0;
            yield break;
        }
        int z = x;
        while (z > 0)
        {
            yield return z % b;
            z = z / b;
        }
    }

    return ToBaseReverse().Reverse();
}

现在添加一种将其转换为一组特定数字的方法:

public static string ToBase(this int number, string digits) =>
    String.Concat(number.ToBase(digits.Length).Select(x => digits[x]));

可以这样使用:

string result = 45.ToBase("0X2Y");
Console.WriteLine(result);

哪个给:

2YX

现在写Enumerable.Range(0, 10).Select(n => n.ToBase("0X2Y"))很简单。

给出:

0, X, 2, Y, X0, XX, X2, XY, 20, 2X

这对所有非零数字都正确计数,除了零本身之外,不显示前导零。