Может ли кто-нибудь дать мне совет о том, как создать рекурсивную версию GetEnumerator ()? Хорошо известная проблема Ханойских башен может служить примером, сравнимым с реальной проблемой, которая у меня есть. Вот простой алгоритм, показывающий все ходы для стека дисков высотой n:
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);
}
}
На самом деле я хочу создать класс HanoiTowerMoves, который реализует IEnumerable и который позволяет мне перебирать все ходы следующим образом:
foreach (Move m in HanoiTowerMoves) Console.WriteLine (m);
Первым шагом к реализации GetEnumerator (), похоже, является избавление от параметров MoveTower. Это легко сделать с помощью стека. Я также представил класс Move, который объединяет параметры в одну переменную.
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);
}
}
Теперь MoveTower можно переписать следующим образом:
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 ();
}
}
Эта версия должна называться следующим образом:
varStack.Push (new Move (n, Needle.A, Needle.B, Needle.Temp));
MoveTower1 ();
Следующим шагом к итерационной версии является реализация класса:
class HanoiTowerMoves : IEnumerable
{
Stack varStack;
int n; // number of disks
public HanoiTowerMoves (int n)
{
this.n = n;
varStack = new Stack ();
}
public IEnumerator GetEnumerator ()
{
// ???????????????????????????? }
// required by the compiler:
IEnumerator IEnumerable.GetEnumerator ()
{
return GetEnumerator ();
}
}
Теперь для меня большой вопрос: что как выглядит тело GetEnumerator ()? Может ли кто-нибудь разгадать за меня эту загадку?
Ниже приведен код Program.cs созданного мной консольного приложения.
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
{
Stack varStack;
int n; // number of disks
public HanoiTowerMoves (int n)
{
this.n = n;
varStack = new Stack ();
}
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 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 ();
}
}
}