Can somebody give me advice on how to create a recursive version of GetEnumerator()? The well-known Towers of Hanoi problem may serve as an example that is comparable to the actual problem I have. A simple algorithm to show all moves for a stack of disks of height n is:
void MoveTower0 (int n, Needle start, Needle finish, Needle temp) { if (n > 0) { MoveTower0 (n - 1, start, temp, finish); Console.WriteLine ("Moving disk from {0} to {1}", start, finish); MoveTower0 (n - 1, temp, finish, start); } }
What I actually want to do is set up a class HanoiTowerMoves that implements IEnumerable and that enables me to iterate over all moves as follows:
foreach (Move m in HanoiTowerMoves) Console.WriteLine (m);
The first step towards a GetEnumerator() implementation seems to get rid of the MoveTower parameters. This can easily be done by using a stack. I also introduced a class Move that combines the parameters into a single variable.
class Move { public int N { private set; get; } public Needle Start { private set; get; } public Needle Finish { private set; get; } public Needle Temp { private set; get; } public Move (int n, Needle start, Needle finish, Needle temp) { N = n; Start = start; Finish = finish; Temp = temp; } public override string ToString () { return string.Format ("Moving disk from {0} to {1}", Start, Finish); } }
Now MoveTower can be rewritten as follows:
void MoveTower1 () { Move m = varStack.Pop (); if (m.N > 0) { varStack.Push (new Move (m.N - 1, m.Start, m.Temp, m.Finish)); MoveTower1 (); Console.WriteLine (m); varStack.Push (new Move (m.N - 1, m.Temp, m.Finish, m.Start)); MoveTower1 (); } }
This version must be called as follows:
varStack.Push (new Move (n, Needle.A, Needle.B, Needle.Temp)); MoveTower1 ();
The next step towards an iterable version is to implement the class:
class HanoiTowerMoves : IEnumerable<Move> { Stack<Move> varStack; int n; // number of disks public HanoiTowerMoves (int n) { this.n = n; varStack = new Stack<Move> (); } public IEnumerator<Move> GetEnumerator () { // ???????????????????????????? } // required by the compiler: IEnumerator IEnumerable.GetEnumerator () { return GetEnumerator (); } }
Now the big question to me is: what does the body of GetEnumerator () look like? Can somebody solve this mystery for me?
Below is the code of Program.cs of the console application I created.
using System; using System.Collections.Generic; using System.Collections; /* Towers of Hanoi * =============== * Suppose you have a tower of N disks on needle A, which are supposed to end up on needle B. * The big picture is to first move the entire stack of the top N-1 disks to the Temp needle, * then move the N-th disk to B, then move the Temp stack to B using A as the new Temp needle. * This is reflected in the way the recursion is set up. */ namespace ConsoleApplication1 { static class main { static void Main (string [] args) { int n; Console.WriteLine ("Towers of Hanoi"); while (true) { Console.Write ("\r\nEnter number of disks: "); if (!int.TryParse (Console.ReadLine (), out n)) { break; } HanoiTowerMoves moves = new HanoiTowerMoves (n); moves.Run (1); // algorithm version number, see below } } } class Move { public int N { private set; get; } public Needle Start { private set; get; } public Needle Finish { private set; get; } public Needle Temp { private set; get; } public Move (int n, Needle start, Needle finish, Needle temp) { N = n; Start = start; Finish = finish; Temp = temp; } public override string ToString () { return string.Format ("Moving disk from {0} to {1}", Start, Finish); } } enum Needle { A, B, Temp } class HanoiTowerMoves : IEnumerable<Move> { Stack<Move> varStack; int n; // number of disks public HanoiTowerMoves (int n) { this.n = n; varStack = new Stack<Move> (); } public void Run (int version) { switch (version) { case 0: // Original version MoveTower0 (n, Needle.A, Needle.B, Needle.Temp); break; case 1: // No parameters (i.e. argument values passed via stack) varStack.Push (new Move (n, Needle.A, Needle.B, Needle.Temp)); MoveTower1 (); break; case 2: // Enumeration foreach (Move m in this) { Console.WriteLine (m); } break; } } void MoveTower0 (int n, Needle start, Needle finish, Needle temp) { if (n > 0) { MoveTower0 (n - 1, start, temp, finish); Console.WriteLine ("Moving disk from {0} to {1}", start, finish); MoveTower0 (n - 1, temp, finish, start); } } void MoveTower1 () { Move m = varStack.Pop (); if (m.N > 0) { varStack.Push (new Move (m.N - 1, m.Start, m.Temp, m.Finish)); MoveTower1 (); Console.WriteLine (m); varStack.Push (new Move (m.N - 1, m.Temp, m.Finish, m.Start)); MoveTower1 (); } } public IEnumerator<Move> GetEnumerator () { yield break; // ???????????????????????????? } /* void MoveTower1 () { Move m = varStack.Pop (); if (m.N > 0) { varStack.Push (new Move (m.N - 1, m.Start, m.Temp, m.Finish)); MoveTower1 (); Console.WriteLine (m); ? yield return m; varStack.Push (new Move (m.N - 1, m.Temp, m.Finish, m.Start)); MoveTower1 (); } } */ // required by the compiler: IEnumerator IEnumerable.GetEnumerator () { return GetEnumerator (); } } }