如何创建范围有限的子枚举器?
假设我有包含 100 个元素的集合。常规枚举器将迭代这 100 个元素。
我想创建枚举器(它基于常规枚举器,即它不是每个集合,而是一种通用方法),其范围从“呵呵”到“那里”——例如,我可以迭代仅中间有 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 个字符串的集合)。 Naive 算法非常简单,但效率也很低。对于~16MB(字符串列表)的集合,我考虑了另一种方法——简单地重新解释数据,而不复制它。因此,我将创建一个迭代器,它从整个集合中选取每个 SIZE_OF_SLICE 元素,并且我还将创建这个作用域迭代器,该迭代器将从第一个迭代器开始并查找 SIZE_OF_SLICE 元素。
这样数据将被就地重用,唯一的区别是你如何迭代它。切片就足够了,而且速度应该很快。
3
我为 IList 实现了高效的切片(一旦你假设你有索引器,这就是小菜一碟),但这让我感到不安,你不能(?)为列表(LinkedList)和数组(List)提供通用的高效算法。因此,如果您正在阅读本文,并且知道如何做到这一点,请不要犹豫回答,即使是在 10 年后(假设 C# 仍然存在)。
Let's say I have collection with 100 elements. Regular enumerator would iterate over those 100 elements.
I would like to create enumerator (which is based on the regular enumerator, i.e. it is not per each collection, but rather one, general approach) which scope is from "hehe" to "there" -- and I could have for example iterate over 20 elements in the middle only.
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);
So in such case when I call "scoped_iter.Reset()" it is reseted to its 0 element (10th for entire collection).
And also it "sees" only elements from 10-30.
The question is -- how to implement such enumerator?
Edit
1.
I need iterator from "here", not from "there", because getting to "there" could be very time consuming. However this is minor thing really, the most problematic is Reset method.
2.
Jon asked about the background. What I really try to achieve is slicing the collection (i.e. you have the -- let's say -- collection of 10 strings, but you would like to interpret it as collection of 5 elements, each element being collection of 2 strings). Naive algorithm is pretty simple, but also is very inefficient. With collection ~16MB (list of strings) I though about another approach -- simply reinterpreting the data, without copying it. So I would create one iterator which picks every SIZE_OF_SLICE element from entire collection, and also I would create this scoped iterator which would start from the first iterator and go for SIZE_OF_SLICE elements.
This way data will be re-used in place, the only difference would be how you iterate over it. It is sufficient for slicing and it should be fast.
3
I implemented efficient slicing for IList (once you assume you have indexer, it is piece of cake) but it is disturbing me, you cannot (?) provide general efficient algorithm for both list (LinkedList) and arrays (List). So if you are reading this, and have an idea how to do it, do not hestitate to answer, even after 10 years (assuming C# will be still with us).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
为了以最少的努力做到这一点,您基本上需要填充一个支持
Reset
(例如List
)的集合,其中使用迭代器,然后返回它。惰性地执行此操作稍微有点棘手 - 即第一次迭代时,填充集合。第一次重置后,进入“重播”模式。我确信这是可行的——只是有点棘手。
如果您必须支持在仅(例如)15 个元素后第一次重置,然后当您第二次遇到第 16 个元素时返回到原始迭代器,那就更棘手了。哎呀。
如果您可以准确地确定您有哪些要求,那么实现它可能是一件有趣的事情...
编辑:只是将一些评论纳入此答案:您通常不能这样做 不复制数据,因为不能保证迭代器完全支持重置。想象一下,如果迭代器正在提供来自某个随机数生成器的数据,或者它是未录制的实时广播 - 显然要重播数据,某些东西必须复制它。
如果您有特定的源实现,那可能会有所不同 - 但您不能仅通过
IEnumerator
来实现界面。To do this with the minimum of effort, you'd basically populate a collection which does support
Reset
(e.g.List<T>
) with the iterator, and then return that.It's slightly trickier to do that lazily - i.e. the first time you iterate, populate a collection. After the first reset, go into "replay" mode. I'm sure it's feasible - it would just be slightly tricky.
It would be even trickier if you had to support being reset the first time after only (say) 15 elements, then when you hit the 16th element the second time round, going back to the original iterator. Yikes.
If you can scope out exactly what requirements you have, it could be a fun thing to implement though...
EDIT: Just to pull some of the comments into this answer: you can't do this in general without copying the data, because there's no guarantee that the iterator will support being reset at all. Imagine if the iterator is providing data from some random number generator, or it's a live broadcast which isn't being recorded - obviously to replay the data, something would have to copy it.
If you have a particular source implementation in mind, that may be different - but you can't do it through just the
IEnumerator<T>
interface.要获得仅看到元素 10-30 的迭代器,请使用
original.Skip(10).Take(20)
,尽管我认为您不能使用Reset
它。如果您需要能够重置它,只需使用类似的东西
To get an iterator that only sees elements 10-30, use
original.Skip(10).Take(20)
, although I don't think you can useReset
on it.If you need to be able to reset it, just use something like