Friday, March 8, 2013

Recursive Yield Return

Was writing a recursive routine the other day and wondering what an implementation of this would look like should I convert it to use yield return.

Much to my consternation this was not as easy as I thought it would be.  It took me almost a week to get it working.  Not of constant time, of course, but elapsed time.  In my initial implementation I could not get my head wrapped around whether each yield return was going to bypass all the calls on the stack and return a result to the caller OR whether it was going to just pop one call context.  Turns out it is the latter, which greatly complicates the implementation.  Given that the implementation of this particular implementation was escaping me.

I was downstairs meditating last week, not thinking about anything in particular and it hit me.  Like a flash...I could see the implementation.  I ran upstairs and quickly wrote down the rough implementation.  I felt kind of like a musician when a riff for a song hits them in their sleep and they need to quickly write it down before they forget it.

I came back to the code after dinner and put the finishing touches on it.

The first method is the seed method implemented as an extension method on IEnumerable.  It in turn calls the the recursive method.

The implementation below is function that given a collection it will return you a collection of all the permutations (a collection of collections). 

For instance if you pass
[
    [ 1, 2, 3],
    [4, 5,  6],
    [7, 8,  9]
]

This routine will return you 3x3x3 (27) collections, each of which will contain 3 items.  Using the data above here are the first few collections returned...
[
   [1, 4, 7],
   [1, 4, 8],
   [1, 4, 9],
   [1, 5, 7],
...
]


  
public static IEnumerable> GetPerm(this IEnumerable> domain)
{
return GetPermRecur(domain);
}
public static IEnumerable> GetPermRecur(IEnumerable> domain)
{
var c = domain.Count();
var firstFromDomain = domain.First();
if (c == 1)
{
foreach (var item in firstFromDomain)
{
yield return new[] {item};
}
}
else
{
var domainWithoutFirst = domain.Skip(1);
var permSoFar = GetPermRecur(domainWithoutFirst);
foreach (var item in firstFromDomain)
{
foreach (var curCol in permSoFar)
{
var curPerm = new List() { item };
curPerm.AddRange(curCol);
yield return curPerm;
}
}
}
}

No comments:

Post a Comment