Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# using LINQ to limit a recursive list

Tags:

c#

linq

I'm trying to figure out how to use LINQ to limit a recursive call.

My intention with the following code is to run through a list of numbers (num) and for each number recursively count/print up to a set amount (6).

the sequence in newnum that I'm trying to get is : 3 4 5 1 2 3 4 5 5 2 3 4 5

but naturally I'm running into an infinite loop instead. The .Where predicate isn't stopping the loop as I had thought and it's probable my base case is off. Any insight as to the proper way to set this up? Thank you.

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));

I noticed that a similar stopping pattern occurs in the following sample code, the .Where will only include factorials less than 7, what am I missing?

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);
like image 579
user1297102 Avatar asked Feb 17 '23 04:02

user1297102


2 Answers

The answer is that you don't have a base case. Once your recursive function is executed, there is nothing to stop it - LINQ doesn't perform any kind of magic that can modify the internal logic of another function.

In the example you are missing this key bit of code that will stop the recursion - the base case:

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

The ternary operator checks to see if n==1 - if it is, it returns 1. This is the base case that your function lacks.

There is no way to provide a base-case to your function through LINQ alone. You need to build this into the recursive function.

Additionally, you are returning the wrong type from your recursive function if you want to return a list of numbers from a single number: this is fundamentally a different case from the Factorial function which returns a single number given a single number.

Here is a function that does what you require without using recursion:

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++;
}
like image 66
Alex Avatar answered Feb 26 '23 19:02

Alex


You could just stick with generating sequences that fits your requirements, something like:

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;

Decent performance and clean code

like image 43
Polity Avatar answered Feb 26 '23 19:02

Polity