Выиграет ли C # от различий между типами перечислителей, такими как итераторы C ++?

StackOverflow https://stackoverflow.com/questions/1634934

  •  06-07-2019
  •  | 
  •  

Вопрос

Я тут думал о том, что IEnumerator.Reset() способ.Я читал в документации MSDN, что она существует только для COM-взаимодействия.Как программисту на C ++, мне это кажется IEnumerator который поддерживает Reset это то, что я бы назвал прямой итератор, в то время как IEnumerator который не поддерживает Reset это действительно входной итератор.

Итак, первая часть моего вопроса заключается в том, правильно ли это понимание?

Вторая часть моего вопроса заключается в том, принесло бы ли это какую-либо пользу в C #, если бы было сделано различие между итераторами ввода и прямыми итераторами (или "перечислителями", если вы предпочитаете)?Разве это не помогло бы устранить некоторую путаницу среди программистов, подобную той, что приведена в этом ИТАК, вопрос о клонировании итераторов?

Редактировать:Разъяснение относительно прямых и входных итераторов.Итератор ввода гарантирует только то, что вы можете перечислить элементы коллекции (или из функции генератора, или из входного потока) только один раз.Именно так работает IEnumerator в C #.Сможете ли вы перечислить во второй раз или нет, определяется тем, сможете ли вы перечислить во второй раз Reset поддерживается.Прямой итератор не имеет этого ограничения.Вы можете перечислять элементы так часто, как захотите.

Некоторые программисты на C # не понимают, почему IEnumerator не может быть надежно использован в многопроходном алгоритме.Рассмотрим следующий случай:

void PrintContents(IEnumerator<int> xs)
{
  while (iter.MoveNext())
    Console.WriteLine(iter.Current); 
  iter.Reset();
  while (iter.MoveNext())
    Console.WriteLine(iter.Current); 
}

Если мы позвоним PrintContents в этом контексте никаких проблем нет:

List<int> ys = new List<int>() { 1, 2, 3 }
PrintContents(ys.GetEnumerator()); 

Однако взгляните на следующее:

IEnumerable<int> GenerateInts() {   
  System.Random rnd = new System.Random();
  for (int i=0; i < 10; ++i)
    yield return Rnd.Next();
}

PrintContents(GenerateInts());

Если IEnumerator поддерживаемый Reset, другими словами, поддерживаются многопроходные алгоритмы, тогда каждый раз, когда вы выполняете итерацию по коллекции, она будет отличаться.Это было бы нежелательно, потому что это было бы неожиданным поведением.Этот пример немного фальшивый, но он действительно имеет место в реальном мире (например,чтение из файловых потоков).

Это было полезно?

Решение

Интересный вопрос.Я полагаю, что, конечно, C # будет выгода.Однако добавить это было бы нелегко.

Это различие существует в C ++ из-за его гораздо более гибкой системы типов.В C # у вас нет надежного универсального способа клонирования объектов, который необходим для представления прямых итераторов (для поддержки многопроходной итерации).И, конечно, чтобы это было действительно полезно, вам также необходимо было бы поддерживать двунаправленные итераторы / перечислители с произвольным доступом.И чтобы все они работали бесперебойно, вам действительно нужна какая-то форма утиного ввода, как в шаблонах C ++.

В конечном счете, сферы применения этих двух концепций различны.

В C ++ предполагается, что итераторы представляют все, что вам нужно знать о диапазоне значений.Учитывая пару итераторов, я не потребность оригинальный контейнер.Я могу сортировать, я могу искать, я могу манипулировать элементами и копировать их столько, сколько мне нравится.Оригинального контейнера на картинке нет.

В C # перечислители предназначены не для того, чтобы делать так много.В конечном счете, они просто предназначены для того, чтобы позволить вам выполнять последовательность действий линейным образом.

Что касается Reset(), широко признано, что было ошибкой добавлять его в первую очередь.Если бы это сработало и было реализовано правильно, тогда да, вы могли бы сказать, что ваш перечислитель аналогичен итераторам пересылки, но в целом лучше игнорировать это как ошибку.И тогда все перечислители похожи только на входные итераторы.

К сожалению.

Другие советы

Reset была большая ошибка. Я называю махинации на IEnumerable<T>. На мой взгляд, правильный способ отразить различие, которое вы проводите между & Quot; forward iterators & Quot; и " входные итераторы " В системе типов .NET есть различие между IEnumerator<T> и <=>.

См. также это ответ , где Эрик Липперт из Microsoft (в неофициальном отношении, без сомнения, моя точка зрения состоит только в том, что он имеет больше полномочий, чем я, чтобы заявить, что это была ошибка дизайна), высказывает аналогичные замечания в комментариях. Также см. Также его потрясающий блог .

Исходя из перспективы C #:

Вы почти никогда не используете IEnumerator напрямую. Обычно вы делаете foreach оператор, который ожидает IEnumerable.

IEnumerable _myCollection;
...
foreach (var item in _myCollection) { /* Do something */ }

Вы тоже не проходите мимо Reset(). Если вы хотите передать коллекцию, которая нуждается в повторении, вы передаете <=>. Поскольку <=> имеет одну функцию, которая возвращает <=>, ее можно использовать для многократного повторения коллекции (несколько проходов).

Нет необходимости в функции <=> для <=>, потому что если вы хотите начать все сначала, вы просто выбрасываете старую (сборщик мусора) и получаете новую.

.NET Framework выиграла бы чрезвычайно, если бы был способ спросить IEnumerator<T> о том, какие способности он может поддерживать и какие обещания он может дать. Такие функции также были бы полезны в IEnumerable<T>, но возможность задавать вопросы перечислителю позволила бы коду, который может получать перечислитель от оболочек, таких как ReadOnlyCollection, использовать базовую коллекцию улучшенными способами без необходимости использования оболочки.

При наличии любого перечислителя для коллекции, который может быть перечислен полностью и не слишком велик, можно создать из него ReadOnlyCollection<T>, который всегда будет давать одинаковую последовательность элементов (в частности, набор оставшихся элементов). в перечислителе) путем считывания всего содержимого в массив, удаления и удаления перечислителя и получения перечислителей из массива (используя его вместо исходного заброшенного перечислителя), оборачивая массив в <=> и возвращая , Хотя такой подход будет работать с любым перечисляемым набором, отвечающим вышеуказанным критериям, он будет ужасно неэффективным для большинства из них. Имея возможность попросить перечислитель передать оставшееся содержимое в неизменяемом <=>, многие перечислители смогут выполнять указанное действие намного более эффективно.

Я так не думаю.Я бы позвонил IEnumerable прямой итератор и входной итератор.Это не позволяет вам вернуться назад или изменить базовую коллекцию.С добавлением foreach ключевое слово, итераторы большую часть времени практически не используются.

Мнение:Разница между входными итераторами (получите каждый из них) противвыходные итераторы (сделайте что-нибудь с каждым из них) слишком тривиален, чтобы оправдывать добавление к фреймворку.Кроме того, чтобы выполнить итератор вывода, вам нужно будет передать делегат итератору.Итератор ввода кажется более естественным программистам на C #.

Есть также IList<T> если программисту нужен произвольный доступ.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top