I am having some trouble making an iterator that can traverse the following type of data structure.
I have a class called Expression, which has one data member, a List<object>.
This list can have any number of children, and some of those children might be other Expression objects.
I want to traverse this structure, and print out every non-list object (but I do want to print out the elements of the list of course), but before entering a list, I want to return "begin nest" and after I just exited a list, I want to return "end nest".
I was able to do this if I ignored the class wherever possible, and just had List<object> objects with List<object> items if I wanted a subExpression, but I would rather do away with this, and instead have an Expressions as the sublists (it would make it easier to do operations on the object. I am aware that I could use extension methods on the List<object> but it would not be appropriate (who wants an Evaluate method on their list that takes no arguments?).
The code that I used to generate the origonal iterator (that works) is:
public IEnumerator GetEnumerator(){
return theIterator(expr).GetEnumerator();
}
private IEnumerable theIterator(object root) {
if ((root is List<object>)){
yield return " begin nest ";
foreach (var item in (List<object>)root){
foreach (var item2 in theIterator(item)){
yield return item2;
}
}
yield return " end nest ";
}
else
yield return root;
}
A type swap of List<object> for expression did not work, and lead to a stackOverflow error. How should the iterator be implemented?
Update: Here is the swapped code:
public IEnumerator GetEnumerator() {
return this.GetEnumerator();
}
private IEnumerable theIterator(object root) {
if ((root is Expression)) {
yield return " begin nest ";
foreach (var item in (Expression)root) {
foreach (var item2 in theIterator(item))
yield return item2;
}
yield return " end nest ";
}
else
yield return root;
}
The reason you are getting a StackOverflowException is that
foreach (var item in (Expression)root)
…internally causes:
((Expression)root).GetEnumerator()
…to be invoked – that is the way that the CLR enumerates the objects that must be assigned to the item variable during each iteration of the foreach loop.
In your case, the GetEnumerator() invocation would cause theIterator to be executed again for the same root, thereby entering an infinite recursion.
To fix your problem, you need to replace:
foreach (var item in (Expression)root)
…with:
foreach (var item in ((Expression)root).expr)
…where expr is the name of the List<object> property.
Why did you change GetEnumerator() to return this.GetEnumerator()?
It seems to me that's where your stack overflow is coming from.
Try making it return theIterator(this).GetEnumerator()?
Related
I've created a templated bst class and I've implemented GetEnumerator but I'm really not happy with what've done.
so first of all I felt i needed an helper function inner visit
private IEnumerable<Node<T>> innerVisit(Node<T> root)
{
if(root== null)
yield break;
if (root.Left != null)
{
var l = innerVisit(root.Left);
foreach (var item in l)
yield return item;
}
yield return root;
if (root.Right != null)
{
var r = innerVisit(root.Right);
foreach (var item in r)
yield return item;
}
}
I really dislike the repeating code but couldn't find a proper solution to it
, clearly repeating the loop is unclean but also I feel that burying it under a function that would jump into this one would be bad practice to say the least. any suggestion on how to do this properly?
also to complete the implementation I've written
public IEnumerator<Node<T>> GetEnumerator()
{
var res = innerVisit(_root);
foreach (var item in res)
yield return item;
}
but that too feels bad and more of a hack to make sure it will work within foreach loop and etc.
I don't think you can solve your problem without repeating the same operations like you did, but I do think you can do it in a clearer way, it is part of the requirements to return an IEumerable and If you want to do it in a recursive way you can't prevent reapiting the yield operations (but you don't have to write all of them by yourself, System.LINQ will help us with that).
We can replace the foreach and the yield return with Enumerable.Concat method, using it we can concat the left InnerVisit IEnumerable, IEnumerable that we will create for the node itself (an array with 1 item of the current node) and the right InnerVisit IEnumerable:
private IEnumerable<Node<T>> InnerVisit(Node<T> node)
{
if(node == null)
{
return Enumerable.Empty<Node<T>>;
}
return InnerVisit(node.Left).Concat(new[] { node }).Concat(InnerVisit(node.Right));
}
Notice that there is no need to check if Left or Right is null before you call the recursive method because it will check it later in the inner call, if it is null we will return an Enumerable.Empty<Node<T>> instead of using yield break as you did.
We can also simplify GetEnumerator by calling GetEnumerator directly on the result of the root InnerVisit, something like this:
public IEnumerator<Node<T>> GetEnumerator()
{
return InnerVisit(_root).GetEnumerator();
}
I have this code:
public static void myMethodMytype paramObject, IEnumerable<MyType> paramObjects)
{
IEnumerable<Mytype> ieFilteredObjects = paramObjects.Where(x=>x.IDType == paramObject.IDType);
if (ieFilteredObjects.Count() == 2)
{
foreach (MyType iterator in ieFilteredObjects)
{
iterator.MyProperty = null;
}
}
}
In this case, ieFilteredObjects has 2 elements, but in the foreach, only update the first element, and the exit of the foreach.
If in the foreach I use this:
foreach (MyType iterator in ieFilteredObjects.ToList())
then it works as expected.
Why I have to convert the IEnumerable to a list?
Thanks.
IEnumerable can only be iterated once. You cannot iterate twice on an IEnumerable and expect to get the same values.
For instance, the IEnumerable may come from a yield return function, each time you call GetEnumerator() on the IEnumerable you start a new call to this function which may create new values.
This is not a common use case, but it exists. I already had this kind of bug in production.
The solution is to cache the result of the first iteration. A common technique is to use a linq: .ToList() to create a IList, that you can iterate ad infinitum
In your code you iterate at least twice:
ieFilteredObjects.Count()
foreach (MyType iterator in ieFilteredObjects)
I wrote this:
using System;using System.Linq;
static class MyExtensions
{
public static IEnumerable<T> Inspect<T> (this IEnumerable<T> source)
{
Console.WriteLine ("In Inspect");
//return source; //Works, but does nothing
foreach(T item in source){
Console.WriteLine(item);
yield return item;
}
}
}
Then went to test it with this:
var collection = Enumerable.Range(-5, 11)
.Select(x => new { Original = x, Square = x * x })
.Inspect()
.OrderBy(x => x.Square)
//.Inspect()
.ThenBy(x => x.Original)
;
foreach (var element in collection)
{
Console.WriteLine(element);
}
The first use of Inspect() works fine. The second one, commented out, won't compile. The return of OrderBy is IOrderedEnumerable. I'd have thought IOrderedEnumerable is-a IEnumerable but, rolling with the punches, I tried:
public static IOrderedEnumerable<T> Inspect<T> (this IOrderedEnumerable<T> source)
{
Console.WriteLine ("In Inspect (ordered)");
foreach(T item in source){
Console.WriteLine(item);
yield return item;
}
}
But this won't compile either. I get told I cannot have an iterator block because System.Linq.IOrderedEnumberable is not an iterator interface type.
What am I missing? I cannot see why people wouldn't want to iterate over an ordered collection the same way they do with the raw collection.
(Using Mono 2.10.8.1, which is effectively C# 4.0, and MonoDevelop 2.8.6.3)
UPDATE:
As joshgo kindly pointed out, I can take an input parameter of IOrderedEnumerable, it does indeed act as-a IEnumerable. But to iterate I must return IEnumerable, and my original error was caused by ThenBy, which insists on being given IOrderedEnumerable. Very reasonable too. But is there a way to satisfy ThenBy here?
UPDATE2:
After playing with the code in both answers (both of which were very helpful), I finally understood why I can't use yield with an IOrderedEnumerable return: there is no point, because the values have to be fully available in order to do the sort. So instead of a loop with yield in it, I may as well use a loop to print out all items, then just return source once at the end.
I believe an explanation of the error can be found here: Some help understanding "yield"
Quoting Lasse V. Karlsen:
A method using yield return must be declared as returning one of the
following two interfaces: IEnumerable or IEnumerator
The issues seems to be with the yield operator and the return type of your second function, IOrderedEnumerable.
If you change the return type from IOrderedEnumerable to IEnumerable, then the 2nd Inspect() call will no longer be an error. However, the ThenBy() call will now throw an error. If you temporarily comment it out, it'll compile but you do lose access to the ThenBy() method.
var collection = Enumerable.Range(-5, 11)
.Select(x => new { Original = x, Square = x * x })
.Inspect()
.OrderBy(x => x.Square)
.Inspect()
//.ThenBy(x => x.Original)
;
foreach (var element in collection)
{
Console.WriteLine(element);
}
...
public static IEnumerable<T> Inspect<T> (this IOrderedEnumerable<T> source)
{
Console.WriteLine ("In Inspect (ordered)");
foreach(T item in source){
Console.WriteLine(item);
yield return item;
}
}
If you want to apply your extension method after operation, which returns IOrdereEnumerable and continue ordering, then you need to create second overloaded extension:
public static IOrderedEnumerable<T> Inspect<T>(this IOrderedEnumerable<T> source)
{
Console.WriteLine("In Ordered Inspect");
// inspected items will be unordered
Func<T, int> selector = item => {
Console.WriteLine(item);
return 0; };
return source.CreateOrderedEnumerable(selector, null, false);
}
What is interesting here:
You need to return IOrderedEnumerable in order to apply ThenBy or ThenByDescending
IOrderedEnumerable is not created via yield return. In your case it could be achieved by creating it from source
You should create dummy selector, which does not break ordering of items
Output will not contain ordered items, because selector is executed in same order as input sequence.
If you want to see ordered items, you need to execute your OrderedEnumerable. This will force executing of all operators, which present before Inspect:
public static IOrderedEnumerable<T> Inspect<T>(this IOrderedEnumerable<T> source)
{
Console.WriteLine("In Ordered Inspect");
var enumerable = source.CreateOrderedEnumerable(x => 0, null, false);
// each time you apply Inspect all query until this operator will be executed
foreach(var item in enumerable)
Console.WriteLine(item);
return enumerable;
}
I am having trouble remembering how (but not why) to use IEnumerators in C#. I am used to Java with its wonderful documentation that explains everything to beginners quite nicely. So please, bear with me.
I have tried learning from other answers on these boards to no avail. Rather than ask a generic question that has already been asked before, I have a specific example that would clarify things for me.
Suppose I have a method that needs to be passed an IEnumerable<String> object. All the method needs to do is concatenate the letters roxxors to the end of every String in the iterator. It then will return this new iterator (of course the original IEnumerable object is left as it was).
How would I go about this? The answer here should help many with basic questions about these objects in addition to me, of course.
Here is the documentation on IEnumerator. They are used to get the values of lists, where the length is not necessarily known ahead of time (even though it could be). The word comes from enumerate, which means "to count off or name one by one".
IEnumerator and IEnumerator<T> is provided by all IEnumerable and IEnumerable<T> interfaces (the latter providing both) in .NET via GetEnumerator(). This is important because the foreach statement is designed to work directly with enumerators through those interface methods.
So for example:
IEnumerator enumerator = enumerable.GetEnumerator();
while (enumerator.MoveNext())
{
object item = enumerator.Current;
// Perform logic on the item
}
Becomes:
foreach(object item in enumerable)
{
// Perform logic on the item
}
As to your specific scenario, almost all collections in .NET implement IEnumerable. Because of that, you can do the following:
public IEnumerator Enumerate(IEnumerable enumerable)
{
// List implements IEnumerable, but could be any collection.
List<string> list = new List<string>();
foreach(string value in enumerable)
{
list.Add(value + "roxxors");
}
return list.GetEnumerator();
}
public IEnumerable<string> Appender(IEnumerable<string> strings)
{
List<string> myList = new List<string>();
foreach(string str in strings)
{
myList.Add(str + "roxxors");
}
return myList;
}
or
public IEnumerable<string> Appender(IEnumerable<string> strings)
{
foreach(string str in strings)
{
yield return str + "roxxors";
}
}
using the yield construct, or simply
var newCollection = strings.Select(str => str + "roxxors"); //(*)
or
var newCollection = from str in strings select str + "roxxors"; //(**)
where the two latter use LINQ and (**) is just syntactic sugar for (*).
If i understand you correctly then in c# the yield return compiler magic is all you need i think.
e.g.
IEnumerable<string> myMethod(IEnumerable<string> sequence)
{
foreach(string item in sequence)
{
yield return item + "roxxors";
}
}
I'd do something like:
private IEnumerable<string> DoWork(IEnumerable<string> data)
{
List<string> newData = new List<string>();
foreach(string item in data)
{
newData.Add(item + "roxxors");
}
return newData;
}
Simple stuff :)
Also you can use LINQ's Select Method:
var source = new[] { "Line 1", "Line 2" };
var result = source.Select(s => s + " roxxors");
Read more here Enumerable.Select Method
I am looking at the following code: http://netrsc.blogspot.com/2010/04/net-c-binary-tree.html
Am I right in thinking that the while (!Found) condition will iterate the tree?
protected void Insert(T item)
{
TreeNode<T> TempNode = Root;
bool Found=false;
while (!Found)
{
int ComparedValue = TempNode.Value.CompareTo(item);
if(ComparedValue<0)
{
if(TempNode.Left==null)
{
TempNode.Left=new TreeNode<T>(item,TempNode);
++NumberOfNodes;
return;
}
else
{
TempNode=TempNode.Left;
}
}
else if(ComparedValue>0)
{
if(TempNode.Right==null)
{
TempNode.Right=new TreeNode<T>(item,TempNode);
++NumberOfNodes;
return;
}
else
{
TempNode=TempNode.Right;
}
}
else
{
TempNode=TempNode.Right;
}
}
}
Also, for the find and traversal methods, how do these work? If nothing is returned from the Traversal method but from the left branch, would the loop in Find execute again? How would it know to execute down the right branch?
protected IEnumerable<TreeNode<T>> Traversal(TreeNode<T> Node)
{
if (Node.Left != null)
{
foreach (TreeNode<T> LeftNode in Traversal(Node.Left))
yield return LeftNode;
}
yield return Node;
if (Node.Right != null)
{
foreach (TreeNode<T> RightNode in Traversal(Node.Right))
yield return RightNode;
}
}
Thanks
An example of iterating the tree is in the Find command, which calls the Traversal function.
foreach (TreeNode<T> Item in Traversal(Root))
The Traversal function will iteratively return the items in the tree in a depth-first, left-to-right manner. If you look at the Traversal code, it calls itself recursively on the left side and then recursively on the right.
Traversal returns the entire tree in an iterable object, where the items are ordered depth-first, left-to-right. The Find command simply loops through each one and when it hits a match returns it breaking out of the loop. Basically, Traversal returns an ordered iterable of items, Find goes through that list looking for a match. Find really doesn't even have to know whether it's searching through a list or a tree or whatever. It just needs something to iterate through to find a match.
Not necessarily. It will only iterate through the nodes on the path of where the inserted node should be added. There are some return statements sprinkled in that loop so it will essentially stop when it find's the correct location and adds the new node. It would have been more appropriate (in the code) to set the Found variable to true instead.
The traversal methods return the nodes of both the left and right subtrees. You should note that it uses yield return and not the plain return. Using it creates an enumerator where each item that is yielded is what the numerator would return as you iterate through it. Think of it as pausing execution when it reaches a yield return statement. When iterating to the next value from the calling code, execution is continued at that point potentially returning more values.
Find will take the results of the traversal and returns the stored value if found in one of the nodes.