One of the most interesting coding challenges given to me for an interview was to create a functional queue. The requirement was that each call to enqueue would create a new queue that containted the old queue and the new item at the tail. Dequeue would also return a new queue and the dequeued item as an out param.
Creating an IEnumerator from this implementation would be nondestructive. And let me tell you implementing a Functional Queue that performs well is a lot more difficult than implementing a performant Functional Stack (stack Push/Pop both work on the Tail, for a Queue Enqueue works on the tail, dequeue works on the head).
My point being...it's trivial to create a nondestructive Stack Enumerator by implementing your own Pointer mechanism (StackNode<T>) and using functional semantics in the Enumerator.
public class Stack<T> implements IEnumerator<T>
{
private class StackNode<T>
{
private readonly T _data;
private readonly StackNode<T> _next;
public StackNode(T data, StackNode<T> next)
{
_data=data;
_next=next;
}
public <T> Data{get {return _data;}}
public StackNode<T> Next{get {return _Next;}}
}
private StackNode<T> _head;
public void Push(T item)
{
_head =new StackNode<T>(item,_head);
}
public T Pop()
{
//Add in handling for a null head (i.e. fresh stack)
var temp=_head.Data;
_head=_head.Next;
return temp;
}
///Here's the fun part
public IEnumerator<T> GetEnumerator()
{
//make a copy.
var current=_head;
while(current!=null)
{
yield return current.Data;
current=_head.Next;
}
}
}
Some things to note. A call to push or pop before the statement current=_head; completes would give you a different stack for enumeration than if there were no multithreading (you might want to use a ReaderWriterLock to safeguard against this). I made the fields in StackNode readonly but of course if T is a mutable object, you can change its values. If you were to create a Stack constructor that took a StackNode as a parameter (and set head to that passed in node). Two stacks constructed in this way will not impact each other (with the exception of a mutable T as I mentioned). You can Push and Pop all you want in one stack, the other will not change.
And that my friend is how you do non-destructive enumeration of a Stack.