【问题标题】:How to create sub-enumerator with limited scope?如何创建范围有限的子枚举器?
【发布时间】:2011-07-28 05:15:08
【问题描述】:

假设我有 100 个元素的集合。常规枚举器将遍历这 100 个元素。

我想创建范围从“hehe”到“there”的枚举器(它基于常规枚举器,即不是每个集合,而是一种通用方法)——我可以例如仅在中间迭代 20 多个元素。

void foo(IEnumerable<int> coll)
{
   var regular_iter = coll.GetEnumerator();
   regular_iter.MoveNext();
   regular_iter.MoveNext();
   // ... 8 more
   var scoped_iter = new ScopeEnumerator(regular_iterator,20);

所以在这种情况下,当我调用“scoped_iter.Reset()”时,它会被重置为其第 0 个元素(整个集合的第 10 个元素)。

而且它也只“看到” 10-30 的元素。

问题是——如何实现这样的枚举器?

编辑

1.

我需要来自“这里”的迭代器,而不是来自“那里”的迭代器,因为到达“那里”可能非常耗时。不过这确实是小事,最大的问题是Reset方法。

2.

Jon 询问了背景。我真正尝试实现的是对集合进行切片(即,你有 - 比方说 - 10 个字符串的集合,但你想将其解释为 5 个元素的集合,每个元素是 2 个字符串的集合)。朴素算法非常简单,但也非常低效。收集~16MB(字符串列表),我想到了另一种方法——简单地重新解释数据,而不复制它。因此,我将创建一个迭代器,它从整个集合中挑选每个 SIZE_OF_SLICE 元素,并且我还将创建这个范围内的迭代器,它将从第一个迭代器开始并选择 SIZE_OF_SLICE 元素。

这样数据将被原地重用,唯一的区别是你如何迭代它。切片就够了,应该很快。

3

我为 IList 实现了高效的切片(一旦你假设你有索引器,它就是小菜一碟)但它让我感到不安,你不能(?)为列表(LinkedList)和数组(List)提供通用的高效算法。因此,如果您正在阅读本文,并且知道如何去做,请不要犹豫回答,即使 10 年后也是如此(假设 C# 仍将与我们同在)。

【问题讨论】:

    标签: c# ienumerator


    【解决方案1】:

    要以最少的努力做到这一点,您基本上可以使用迭代器填充一个确实支持Reset(例如List&lt;T&gt;)的集合,然后返回它。

    懒惰地这样做有点棘手 - 即第一次迭代时,填充一个集合。第一次重置后,进入“重播”模式。我确信这是可行的——只是有点棘手。

    如果您必须支持仅在(例如)15 个元素后第一次重置,然后当您第二次遇到第 16 个元素时,返回到原始迭代器,那将更加棘手。哎呀。

    如果您可以准确地确定您的需求,那么实现它可能是一件有趣的事情......

    编辑:只是为了将一些 cmets 拉到这个答案中:一般不复制数据就不能这样做,因为不能保证迭代器完全支持被重置。想象一下,如果迭代器正在提供来自某个随机数生成器的数据,或者它是一个没有被记录的直播 - 显然要重放数据,某些东西必须复制它。

    如果您有特定的源代码实现,那可能会有所不同 - 但您不能仅通过IEnumerator&lt;T&gt; 接口来实现。

    【讨论】:

    • 嗯,我没完全明白。当您编写“填充集合”时,您的意思是作用域迭代器?但这也意味着复制大量数据,因为您不能事先保证集合的元素只是一个类(例如)。我只需要迭代,没有数据复制。它必须尽可能轻。
    • @macias:那你就卡住了。原来的迭代器可能不支持被重置,即使在作用域迭代器被重置时你也不知道它的原始位置跳到那个位置。
    • 这是一个非常悲伤的消息。这意味着如果您有 1T 的数据,并且您想将其查看为 2D 数组,则必须复制这 1T 的数据。哎哟!我等一下,也许有人会想出奇迹答案 ;-) 如果没有,你的评论就是答案。 Sick SO -- 我不能以“@Jon”或“@Jon Skeet”开始我的评论,你没有被禁止,不是吗? ;-).
    • @macias:如果您有一个非常具体的源类型,您可能能够对其进行排序。但是想象一下,如果数据是从某个地方流式传输的——可能没有“重置”的概念。 (想象一下它是一个随机数流。)所以你不能一般这样做。
    • @macias:对,正如我所说,考虑到特定的源类型,您也许可以做到。但我不会尝试从 IEnumerator&lt;T&gt; 的起点开始 - 我会从 ICollection&lt;T&gt; 创建“切片”。
    【解决方案2】:

    要获得只能查看元素 10-30 的迭代器,请使用 original.Skip(10).Take(20),尽管我认为您不能在其上使用 Reset

    如果您需要能够重置它,只需使用类似

    original.Skip(10).Take(20).ToArray()
    

    【讨论】:

    • 很多迭代器不支持Reset。它只是为了与 COM 兼容而存在,不应依赖它。 “重置”迭代器的唯一可靠方法是创建一个新的。
    • 事实上,任何使用迭代器块创建的迭代器都不能被依赖。
    • @Sven,假设我可以创建一个新的,但是如何?我无法重复原件。
    猜你喜欢
    • 2011-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-31
    • 1970-01-01
    相关资源
    最近更新 更多