GetEnumerator()の再帰バージョンを作成する方法に関するアドバイスをいただけますか? よく知られているTowers of Hanoi problemは、私が実際に抱えている問題に匹敵する例として役立ちます。高さnのディスクのスタックのためのすべての動きを表示する単純なアルゴリズムは次のとおりです。C#GetEnumerator()の再帰バージョンを作成する方法
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);
}
}
私は実際にIEnumerableを実装するクラスのHanoiTowerMovesを設定されてやりたい、それが次のようにすべての動きを反復処理するために私を可能にします:
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<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();
}
}
今私にとって大きな疑問は、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<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();
}
}
}
私は、GetEnumeratorを再帰的に呼び出す点について理解していません。このメソッドを呼び出す再帰関数を書いてください。難しくありません。 –