Ienumerable and Recursion Using Yield Return

IEnumerable and Recursion using yield return

Inside a method that returns IEnumerable<T>, yield return has to return T, not an IEnumerable<T>.

Replace

yield return c.GetDeepControlsByType<T>();

with:

foreach (var x in c.GetDeepControlsByType<T>())
{
yield return x;
}

Yield return in local recursive function

Nested methods don't behave any differently to regular methods with respect to the effect of yield return.

It's not that the methods are ignored - it's that you're not using the value returned from them, which makes them pointless. In fact, you won't even enter the body of the method recursively, because that doesn't happen until you call MoveNext() on the enumerator returned by the GetEnumerator() call to the returned IEnumerable<>.

Unfortunately C# doesn't have a sort of "yield foreach" syntax, so you have to iterate over all the values and yield them directly:

public IEnumerable<ElementType> GetSubtreeFlattenedPostOrder()
{
return PostOrderRecursive(this);

IEnumerable<ElementType> PostOrderRecursive(BinaryTree<ElementType> currentNode)
{
if (currentNode.HasLeft)
{
foreach (var node in PostOrderRecursive(currentNode.Left))
{
yield return node;
}
}
if (currentNode.HasRight)
{
foreach (var node in PostOrderRecursive(currentNode.Right))
{
yield return node;
}
}

yield return currentNode.element;
}
}

One point to note: this implementation performs poorly, because it needs to create so many iterators. An implementation which avoided recursion but kept a stack or queue of nodes to process could be a lot more efficient. You should bear that in mind if you have any large trees, but it may make sense to keep it simple if performance isn't an issue.

Recursion and Yield return

I can think of at least two alternatives which would be better than what you're doing now:

  1. Build a single List<T> object recursively, by passing the object to the recursive method.
  2. Don't build a List<T> object at all; instead, just recursively evaluate iterators.

Example of #1 (I did not bother to try to clean up any but the most obvious bugs/syntax errors in the original code):

private IEnumerable<object> HandleChildItems()
{
var itemsList = new List<Item>();

GetChildItems(xmlnode, itemsList);

// IEnumerable<T> is covariant
return itemsList;
}

private void GetChildItems(XMLnodeList nodeList, List<Item> itemsList)
{
// Here I recursively call the method to add the items to list
foreach (xmlnode xn in nodeList)
{
if (xn.childnodes.count > 0)
{
GetChildItems(xn.childnodes, itemsList);
}
else
{
itemsList.Add(new Item { Code = "123", Itemtext = "xyz" });
}
}
}

Example of #2:

private IEnumerable<object> HandleChildItems()
{
foreach (Item item in GetChildItems(xmlnode))
{
yield return item;
}
}

private IEnumerable<Item> GetChildItems(XMLnodeList nodeList)
{
// Here I recursively call the method to add the items to list
foreach (xmlnode xn in nodeList)
{
if (xn.childnodes.count > 0)
{
foreach (Item item in GetChildItems(xn.childnodes))
{
yield return item;
}
}
else
{
yield return new Item { Code = "123", Itemtext = "xyz" };
}
}
}

Example #1 has the advantage that you create just one intermediate object, the List<Item> itself. But it does require that the entire list be stored in memory at once.

Example #2 creates a potentially large number of iterator method objects, one for each level of recursion. But this number is likely much smaller than the total number of Item objects you would have to create, and you do avoid having to store all those Item objects in memory all at once.

C# how to use yield operator during recursive call?

You need to iterate your recursive call also. In your code, you only return the base case while the rest of the executions don't return anything.

private static IEnumerable<int[]> Combinations(int[] input, int len, int startPosition, int[] result)
{
if (len == 0)
{
yield return result;
}
else
{
for (int i = startPosition; i <= input.Length - len; i++)
{
result[result.Length - len] = input[i];

//// You need to return the results of your recursive call
foreach (var combination in Combinations(input, len - 1, i + 1, result))
{
yield return combination;
}
}
}
}

C# recursive yield return not returning anything

Option 1, yield each value from the recursive call.

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

Option 2, reuse existing IEnumerable types built into the framework to avoid yield return completely;

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

Option 3, use nested loops instead of a recursive method, left as an exercise for the reader.

Does yield return have any uses other than for IEnumerable?

Your question

Does C#'s yield return have a use outside of IEnumerables

The answer is no, you can see this by the yield documentation

yield (C# Reference)

When you use the yield contextual keyword in a statement, you indicate
that the method, operator, or get accessor in which it appears is an
iterator. Using yield to define an iterator removes the need for an
explicit extra class (the class that holds the state for an
enumeration, see IEnumerator for an example) when you implement the
IEnumerable and IEnumerator pattern for a custom collection type.

Iterator methods and get accessors

The declaration of an iterator must meet the following requirements:

>>>The return type must be IEnumerable, IEnumerable, IEnumerator, or IEnumerator <<<

IEnumerable yield and recursion - doesn't work

You need to iterate though the enumerable returned by the recursive call and yield return each of the items explicitly.

    private IEnumerable<int> fibRec(int a, int b)
{
int tmp = a;
a = b;
b = tmp + b;
yield return a;
foreach(int val in fibRec(a, b))
{
yield return val;
}
}

Recursion with yield return elements order in tree

Have you tried something like:

private IEnumerable<Node> getAllNodesRecursively(Node subnode) 
{
// Return the parent before its children
yield return subnode;

foreach (Node node in subnode.Nodes)
{
foreach(Node n in getAllNodesRecursively(node))
{
yield return n;
}
}
}

Your implementation is calling getAllNodesRecursively recursively, but ignoring its return value.



Related Topics



Leave a reply



Submit