4

我试图弄清楚如何使用 LINQ 来限制递归调用。

我使用以下代码的目的是遍历数字列表 ( num),并为每个数字递归地计数/打印到设定的数量 ( 6)。

newnum我想要得到的顺序是:3 4 5 1 2 3 4 5 5 2 3 4 5

但我自然而然地陷入了无限循环。谓词并没有像我想象的.Where那样停止循环,很可能我的基本情况已经关闭。关于设置它的正确方法的任何见解?谢谢你。

var num = new[] {3, 1, 8, 5, 2};

    Func<int, int> writeString = delegate(int count)
                        {                       
                            Func<int, int> recursiveWrite = null;
                            recursiveWrite = n => 
                                                {
                                                    Console.WriteLine("string " + n); 

                                                    recursiveWrite(n+1);
                                                    return n;
                                                };
                            return recursiveWrite(count);
                        };

    var newnum = num.Where(n => writeString(n) < 6);   // is this possible?
    newnum.ToList().ForEach( w => Console.WriteLine(w));

我注意到下面的示例代码中出现了类似的停止模式,.Where它只包括小于 7 的阶乘,我错过了什么?

var numbers = new[] { 5,1,3,7,2,6,4};

Func<int, int> factorial = delegate(int num) {
        Func<int, int> locFactorial = null;
        locFactorial = n => n == 1 ? 1 : n * locFactorial(n - 1);
        return locFactorial(num);
};

var smallnums = numbers.Where(n => factorial(n) < 7);
4

4 回答 4

4

答案是您没有基本案例。一旦执行了递归函数,就没有什么可以阻止它了——LINQ 不会执行任何可以修改另一个函数的内部逻辑的魔法。

在示例中,您缺少将停止递归的关键代码位 - 基本情况:

locFactorial = n => n == 1 ? 1 : n * locFactorial(n - 1);

三元运算符检查是否n==1- 如果是,则返回 1。这是您的功能缺乏的基本情况。

仅通过 LINQ 无法为您的函数提供基本案例。您需要将其构建到递归函数中。

此外,如果要从单个数字返回数字列表,Factorial则从递归函数返回错误的类型:这与在给定单个数字的情况下返回单个数字的函数根本不同。

这是一个无需使用递归即可完成您需要的功能:

void Main()
{
    var numbers = new[] {3, 1, 8, 5, 2};

    numbers.SelectMany(x => GetIncreasing(x).TakeWhile(y => y < 6));
}

IEnumerable<int> GetIncreasing(int x)
{
   while (true)
       yield return x++;
}
于 2013-03-14T10:26:28.773 回答
2

您可以坚持生成符合您要求的序列,例如:

var num = new[] { 3, 1, 8, 5, 2 };
var limit = 6;

var query = from n in num
            where n < limit // sanity check
            from pn in Enumerable.Range(n, limit - n)
            select pn;

体面的性能和干净的代码

于 2013-03-14T10:41:26.880 回答
1

与阶乘样本的不同之处在于结束条件的放置。这是你应该做的:

recursiveWrite = n => 
                    {
                        Console.WriteLine("string " + n);
                        if (n < 6)
                            recursiveWrite(n+1);
                        return n;
                    };
于 2013-03-14T10:28:04.410 回答
1

不完全确定您要达到的目标,但我希望这会有所帮助。您需要递归 lambda 中的停止条件(如阶乘中的 n==1)。使用嵌套函数,您可以“动态地”注入此限制。

class Program
{
    static void Main(string[] args)
    {
        var num = new[] { 3, 1, 8, 5, 2 };
        Func<int, Func<int, IEnumerable<int>>> writeString = 
            delegate(int maxcount)
            {
                Func<int, IEnumerable<int>> recursiveWrite = null;
                recursiveWrite = (n) =>
                    {
                        if (n < maxcount)
                        {
                            Console.WriteLine("string " + n);
                            var rec = recursiveWrite(n + 1);
                            return new List<int>(){n}.Concat(rec);
                        }
                        return new List<int>();
                    };
                return recursiveWrite;
            };

        var newnum = num.SelectMany(n => writeString(6)(n));   // is this possible?
        newnum.ToList().ForEach(w => Console.WriteLine(w));
        Console.ReadLine();
    }
}
于 2013-03-14T10:34:31.783 回答