Как создать суб-перечислитель с ограниченной областью действия? - PullRequest
1 голос
/ 28 июля 2011

Допустим, у меня есть коллекция из 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

Джон спросил о фоне. Чего я действительно пытаюсь добиться, так это нарезать коллекцию (т. Е. У вас есть, скажем, коллекция из 10 строк, но вы хотели бы интерпретировать ее как коллекцию из 5 элементов, каждый из которых представляет собой коллекцию из 2 строк). Наивный алгоритм довольно прост, но также очень неэффективен. С коллекцией ~ 16MB (список строк) я подумал о другом подходе - просто переосмыслить данные, не копируя их. Поэтому я бы создал один итератор, который выбирает каждый элемент SIZE_OF_SLICE из всей коллекции, а также я бы создал этот ограниченный итератор, который начинался бы с первого итератора и использовался для элементов SIZE_OF_SLICE.

Таким образом, данные будут повторно использоваться на месте, единственное отличие будет в том, как вы их итерируете. Это достаточно для нарезки и это должно быть быстро.

3

Я реализовал эффективное разделение для IList (если вы предполагаете, что у вас есть индексатор, это очень просто), но это меня беспокоит, вы не можете (?) Предоставить общий эффективный алгоритм для списков (LinkedList) и массивов (List). Поэтому, если вы читаете это и у вас есть идея, как это сделать, не стесняйтесь отвечать, даже через 10 лет (при условии, что C # все еще будет с нами).

Ответы [ 2 ]

2 голосов
/ 28 июля 2011

Чтобы сделать это с минимальными усилиями, вам нужно заполнить коллекцию, которая поддерживает Reset (например, List<T>) с помощью итератора, а затем вернуть ее.

Это немного сложнее сделать лениво - т.е. при первой итерации, заполнить коллекцию. После первого сброса перейдите в режим «воспроизведения». Я уверен, что это выполнимо - это было бы немного сложно.

Было бы еще сложнее, если бы вам пришлось поддерживать сброс в первый раз только после, скажем, 15 элементов, а затем, когда вы ударяете 16-й элемент во второй раз, возвращаясь к исходному итератору. Хлоп.

Если вы можете точно определить, какие у вас требования, реализовать это было бы забавно ...

РЕДАКТИРОВАТЬ: Просто чтобы включить некоторые комментарии в этот ответ: вы не можете сделать это в общем без копирования данных, потому что нет никакой гарантии, что итератор вообще будет поддерживать сброс. Представьте себе, если итератор предоставляет данные от какого-то генератора случайных чисел, или это прямая трансляция, которая не записывается - очевидно, чтобы воспроизвести данные, что-то должно будет скопировать их.

Если вы имеете в виду конкретную исходную реализацию , это может отличаться - но вы не можете сделать это через просто IEnumerator<T> интерфейс.

2 голосов
/ 28 июля 2011

Чтобы получить итератор, который видит только элементы 10-30, используйте original.Skip(10).Take(20), хотя я не думаю, что вы можете использовать Reset для него.

Если вам нужно сбросить настройки, просто используйте что-то вроде

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