- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
有人可以就如何创建递归版本的 GetEnumerator() 给我建议吗?家喻户晓Towers of Hanoi problem可以作为与我遇到的实际问题相当的例子。显示高度为 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);
}
}
我真正想做的是设置一个实现 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 ();
}
}
}
最佳答案
您的方法很好,但我认为您在某种程度上考虑了这个问题。让我们退后一步。你有一个递归算法:
void MoveTowerConsole (int n, Needle start, Needle finish, Needle temp)
{
if (n > 0)
{
MoveTowerConsole (n - 1, start, temp, finish);
Console.WriteLine ("Moving disk from {0} to {1}", start, finish);
MoveTowerConsole (n - 1, temp, finish, start);
}
}
算法的输出是一堆控制台输出。相反,假设您希望算法的输出是将要输出到控制台的字符串序列。让我们来推理一下这样的方法会是什么样子。
首先,我们将重命名它。其次,它的返回类型不能为空。必须是 IEnumerable<string>
:
IEnumerable<string> MoveTower(int n, Needle start, Needle finish, Needle temp)
{
if (n > 0)
{
MoveTower(n - 1, start, temp, finish);
Console.WriteLine ("Moving disk from {0} to {1}", start, finish);
MoveTower(n - 1, temp, finish, start);
}
}
这样对吗?不,我们没有返回任何东西,我们仍然在向控制台转储。 我们希望迭代器产生什么?我们希望迭代器产生:
因此我们修改算法以产生这些:
IEnumerable<string> MoveTower(int n, Needle start, Needle finish, Needle temp)
{
if (n > 0)
{
foreach(string move in MoveTower(n - 1, start, temp, finish))
yield return move;
yield return string.Format("Moving disk from {0} to {1}", start, finish);
foreach(string move in MoveTower(n - 1, temp, finish, start))
yield return move;
}
}
我们完成了!就这么简单。无需定义整个类即可将递归算法转换为递归枚举器;让编译器为您完成这项工作。
如果你想把它变成一个枚举“移动”的方法,那么这样做:
IEnumerable<Move> MoveTower(int n, Needle start, Needle finish, Needle temp)
{
if (n > 0)
{
foreach(Move move in MoveTower(n - 1, start, temp, finish))
yield return move;
yield return new Move(start, finish);
foreach(Move move in MoveTower(n - 1, temp, finish, start))
yield return move;
}
}
现在,我会根据效率批评这段代码。通过以这种方式制作递归枚举器,您所做的就是构建一个 n 个枚举器链。当你需要下一个项目时,顶部的枚举器调用下一个枚举器调用下一个枚举器......向下到底部,n 深。所以现在每一步实际上需要n步才能完成。出于这个原因,我倾向于不使用递归来解决问题。
练习:重写上面的迭代器 block ,使其完全不进行递归。您使用显式堆栈的解决方案是朝着正确方向迈出的一步,但它仍然会进行递归。您能否调整它以便不进行递归?
如果您一心想编写一个实现 IEnumerable<Move>
的类那么您可以直接修改上面的代码:
class MoveIterator : IEnumerable<Move>
{
public IEnumerator<Move> GetEnumerator()
{
foreach(Move move in MoveTower(whatever))
yield return move;
}
您可以使用 yield return 来实现返回枚举数或可枚举数的方法。
关于C# 如何制作 GetEnumerator() 的递归版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8821814/
在 .net 框架中,有一个通用的 IEnumerable继承自非泛型 IEnumerable 的接口(interface),并且他们都有 GetEnumerator()方法。这两者之间的唯一区别Ge
致力于实现 IEnumerable 接口(interface)。我实际上找到了两个需要实现的方法: public IEnumerator GetEnumerator() System.Collecti
查看这段代码: public class myWords : IEnumerable { string[] f = "I love you".Split(new string[]{"lo"},
基本上假设您有一些收藏: public class FurCollection : IEnumerable { public IEnumerator GetEnumerator() {
List为什么要定义这三个方法? public Enumerator GetEnumerator() => new Enumerator(this); IEnumera
我对 Reflection.Emit 有疑问。我想要动态创建的类,它具有 ICollection 的简单实现。我定义的所有方法都很好,而不是接下来的两个: public IEnumerator Get
来自 SortedSet.GetEnumerator 文档: This method is an O(1) operation 来自 SortedDictionary.GetEnumerator 文档
我正在编写一个应该能够显示任何数据列表的控件。我想要做的是模仿 for-in 构造,因为我检查包含 Current 属性和 MoveNext 方法的公共(public) GetEnumerator 函
我正在使用 JSOM 查找特定列表中特定项目的特定字段中的值。总的来说,代码查找用户的组成员身份(使用 SPServices),然后使用该值来匹配列表中的项目(列表包含填充有组名称的“Title”字段
我不能重载 GetEnumerator () 吗 IEnumerator IEnumerable.GetEnumerator ( T[] val1,T[] val2) { .... some co
我正在为 C# 开发并发字典实现,我想知道这个 GetEnumerator() 实现是否实际上是(线程)安全的。 它并没有做一个实际的快照,所以我想知道它是否会在以后读/写内部字典时搞砸,或者它是否会
我有一个 BinaryTree类和一个BinaryTreeNode对于持有节点,我已经制作了树并为它编写了前序、后序和中序方法。 但我不知道怎么写IEnumerator为此(我只想对 GetEnume
假设我有一个非常 长的字符串,并且想要获取一个从字符串中间的给定索引处开始的枚举器(为了 LINQ 的优点)。也就是说, var longString = GetLongString(); var i
我想将枚举器用于 Delphi XE2 的通用集合。我想知道,谁拥有函数 GetEnumerator 返回的 TEnumerator (我在文档中没有找到任何明确的答案): 我是否拥有它并需要在使用后
在我们的代码库中,我们有一个数据访问层,提供类似于以下的方法: public IEnumerable GetFoos() { var collection = new Collection()
我真的不明白为什么在这个例子中,这个类定义了两个GetEnumerator方法:一个显式实现接口(interface),另一个隐式 如我所知。那为什么? class FormattedAddresse
我有一个简单的代码,目的是在文件不存在或其中的数据版本已过时时通过文件或数据库返回枚举器。简单的。但是,我正在为该方法的工作原理而苦苦挣扎。原因如下: public override IEnumera
我尝试在 SharePoint 中接收用户子网站的子网站。在第一次调用接收子网站(函数 getSubWebs())时,一切正常:子网站已接收,我可以遍历它们。 但是当我尝试使用相同类型的代码获取这些网
我想在哈希集上使用 foreach,但 VS 不允许。 这是我到目前为止所拥有的: { class Team { private HashSet team; public Team()
我想遍历更新面板的各种控件。在使用控件 ID 进行迭代时,我想删除一些控件。 但我不知道如何使用 GetEnumerator 方法遍历更新面板的控件? 如果我们可以通过其他方式进行迭代,请告诉我。 最
我是一名优秀的程序员,十分优秀!