逻辑是无情的! IEnumerable不支持Clone,你需要Clone,所以你不应该使用IEnumerable。
或者更准确地说,您不应该将它用作在 Scheme 解释器上工作的基础。为什么不做一个简单的不可变链表呢?
public class Link<TValue>
{
private readonly TValue value;
private readonly Link<TValue> next;
public Link(TValue value, Link<TValue> next)
{
this.value = value;
this.next = next;
}
public TValue Value
{
get { return value; }
}
public Link<TValue> Next
{
get { return next; }
}
public IEnumerable<TValue> ToEnumerable()
{
for (Link<TValue> v = this; v != null; v = v.next)
yield return v.value;
}
}
请注意,ToEnumerable 方法可以方便地以标准 C# 方式使用。
回答你的问题:
任何人都可以提出一个现实的,
有用的 IEnumerable 示例
无法做到的对象
提供有效的“Clone()”方法?
会不会有问题
“收益”结构?
IEnumerable 可以在世界任何地方获取其数据。这是一个从控制台读取行的示例:
IEnumerable<string> GetConsoleLines()
{
for (; ;)
yield return Console.ReadLine();
}
这样做有两个问题:首先,Clone 函数编写起来并不特别简单(而Reset 将毫无意义)。其次,序列是无限的——这是完全可以允许的。序列是惰性的。
另一个例子:
IEnumerable<int> GetIntegers()
{
for (int n = 0; ; n++)
yield return n;
}
对于这两个示例,您接受的“解决方法”没有多大用处,因为它只会耗尽可用内存或永远挂断。但这些都是完全有效的序列示例。
要了解 C# 和 F# 序列,您需要查看 Haskell 中的列表,而不是 Scheme 中的列表。
如果你认为无限的东西是一个红鲱鱼,那么从套接字读取字节怎么样:
IEnumerable<byte> GetSocketBytes(Socket s)
{
byte[] buffer = new bytes[100];
for (;;)
{
int r = s.Receive(buffer);
if (r == 0)
yield break;
for (int n = 0; n < r; n++)
yield return buffer[n];
}
}
如果有一些字节数通过套接字发送,这将不是一个无限序列。然而,为此编写克隆将非常困难。编译器如何自动生成 IEnumerable 实现?
一旦创建了克隆,两个实例现在都必须从它们共享的缓冲区系统中工作。这是可能的,但实际上它不是必需的——这不是这些序列的设计使用方式。您将它们纯粹“功能性地”对待,就像值一样,递归地对它们应用过滤器,而不是“强制性地”记住序列中的位置。它比低级的car/cdr 操作要干净一些。
另一个问题:
我想知道,最低级别是什么
“原始人”我需要这样
我可能想做的任何事情
我的 Scheme 解释器中的 IEnumerable
可以在方案中实施,而不是
而不是内置。
我认为简短的答案是查看Abelson and Sussman,尤其是the part about streams。 IEnumerable 是一个流,而不是一个列表。它们描述了您如何需要特殊版本的地图、过滤器、累积等来使用它们。他们还在第 4.2 节中提出了统一列表和流的想法。