Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive IEnumerable doesn't work as expected?

I've written a recursive function which yields IEnumerable<int>

IEnumerable<int>   Go(int b  )
{
 if (b==0) yield  return 0;
   else
 foreach (var element in Go(b-1))
  {
    yield return element;
  }
}

So If I write

foreach (var element in Go(3))
{
    Console.WriteLine (element);
}

It should yield

0
1
2
3

But it doesn't work as expected. ( it displays 0).

In normal recursive function ( which return int - without Ienumerable) it works fine.

Question:

How can I fix the code so it yields the expected value ?

nb. No there's no reason for using recursive Ienumerables. It's just came to my mind after playing with recursive yields.

like image 635
Royi Namir Avatar asked Mar 18 '15 12:03

Royi Namir


4 Answers

Because you never yield b itself but only yield 0.

IEnumerable<int> Go(int b)
{
    if(b > 0) {
         foreach (var element in Go(b-1))
            yield return element;
    }
    yield return b;
}

Note that if you want the resulting sequence to start from 0 and go upwards you have to yield return b after the foreach. Let's unroll the first call for Go(3):

Go(3):
foreach(var element in Go(2)):
    yield return element;
yield return 3;

So 3 will be the last item in the sequence (because all the others are yieled before it). Let's now unroll Go(2):

Go(3):
    Go(2):
    foreach(var element in Go(1)):
        yield return element;
    yield return 2;
yield return 3;

Go(1):

Go(3):
    Go(2):
        Go(1):
            foreach(var element in Go(0))
                yield return element;
        yield return 1;
    yield return 2;
yield return 3;

As you can see, result are chained "backwards" with respect to the calls:

Go(3) --> Go(2) --> Go(1) --> Go(0) --> 0 --> 1 --> 2 --> 3
like image 106
BlackBear Avatar answered Oct 02 '22 07:10

BlackBear


I doubt that it would work anything different - because the only concrete yield I see is yield 0

I guess you want something like this:

IEnumerable<int> Go(int b)
{
   if (b > 0)
   {
      foreach (var element in Go(b-1))
      {
        yield return element;
      }
   }
   yield return b;
}

but still this is highly inefficient and will blow the stack with bigger bs


For your question:

Your code:

will do this:

b=3:
  is b == 0? no ok, then enumerate and return everything from b=2...
     b=2:
       is b == 0? no ok, then enumerate and return everything from b=1...
         b=1:
           is b == 0? no ok, then enumerate everything from b=0...
             b=0:
               is b == 0? **YES** so yield a single **0**
           everything was {0}
       everything was {0}
  everything was {0}
return is {0}
like image 30
Random Dev Avatar answered Oct 02 '22 08:10

Random Dev


Yet another variant with condition b==0

static IEnumerable<int> Go(int b)
{
    if (b == 0)
    {
        yield return 0; //return 0 if b==0;
        yield break; // say that iteration end;
    }

    foreach (var el in Go(b - 1)) 
    {
        yield return el;
    }

    yield return b; //return current b as element of result collection

}

or without yield break

static IEnumerable<int> Go(int b)
{
    if (b == 0)
    {
        yield return 0;
    }
    else
    {

        foreach (var el in Go(b - 1)) 
        {
            yield return el;
        }

        yield return b; //return current b as element of result collection
    }
}
like image 39
Grundy Avatar answered Oct 02 '22 07:10

Grundy


Why your code doesn't work... Let's start from the end... You want [0, 1, 2, 3]. Clearly to obtain that sequence, there must be a

yield return 0
yield return 1
yield return 2
yield return 3

But in your code you can:

yield return 0

or

yield return the Go function 

nowhere you have some code that can yield return the non-zero value!

Note in fact that correct code has a

yield return b

where b is the value passed to the function Go(int b), so that the function will first call itself recursively to return the valeus 0...b-1 and then yield the b value.

like image 23
xanatos Avatar answered Oct 02 '22 08:10

xanatos