Перечисление против индексации против итерации

Я читаю блог Эрика Липперта уже некоторое время (он превосходен, вам стоит его проверить) и в комментариях одного из его posts, он упоминает, что не собирался индексировать последовательность чисел, а только перечислять их.

В чем разница между перечислением и индексацией, я везде искал? Во время моих поисков я еще больше запутался, когда в уравнение была введена Итерация? Может кто-нибудь объяснить эти 3 концепции, может быть, даже привести пример? Прежде чем вы пометите это как обман, я уже видел несколько вопросов по «Итератору против перечислителя», однако я еще не видел правильного объяснения (отсюда и вопрос). Я ценю вашу помощь.


person Dimitar Dimitrov    schedule 11.05.2013    source источник


Ответы (3)


В комментарии к статье Эрик ответил на наблюдение, что, поскольку размер перестановки растет экспоненциально, она быстро перерастет числа, представляемые 32 битами. Эрик ответил, что он не собирается индексировать перестановки, под которыми он имел в виду определение схемы нумерации для получения порядкового номера перестановки. Вот почему, по его словам, переполнение 32 битов не было одной из его забот: его подход позволял перечислять или просто "производить" все перестановки в некотором порядке, как в отличие от предоставления способа получить N-th перестановку в соответствии с некоторой схемой нумерации.

Сравните это с проблемой, обсуждаемой в вопросе о создании N-th перестановок без прохождения всех предыдущих: здесь автор хочет индексировать или присваивать номера перестановкам, поэтому размер целого числа имеет для них значение.

Вот пример перестановок индексации, обсуждавшихся в вопросе, указанном выше:

1 ABC
2 ACB
3 BAC
4 BCA
5 CAB
6 CBA

Эта схема индексации позволяет ответить на два вопроса:

  • Каков номер конкретной перестановки, скажем, BCA? (это 4)
  • Что такое номер перестановки X, скажем, 5? (это CAB)

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

person Sergey Kalinichenko    schedule 11.05.2013
comment
Отличный материал, я ценю вашу помощь. Теперь, когда я смотрю на это, это совершенно очевидно... Но как насчет итерации, чем она отличается от перечисления? - person Dimitar Dimitrov; 11.05.2013
comment
@DimitarDimitrov Перечисление и итерация в этом контексте очень тесно связаны: перечисление означает создание всех перестановок, а итерация означает прохождение их в цикле. Чтобы перечислить последовательность, вам нужно знать, как это сделать; чтобы повторить это, вам нужно на самом деле это сделать. В контексте C# можно перечислить последовательность, предоставив IEnumerabl<T> с отложенным выполнением. Затем у него будет выбор: повторить эту последовательность, скажем, с помощью цикла foreach, или сделать что-то еще — например, взять первые несколько элементов и отбросить остальную часть перечисления. - person Sergey Kalinichenko; 11.05.2013
comment
Итак, подведем итог: перечисление — это действие по созданию последовательности перестановок, а итерация — это действие по прохождению последовательности? Кроме того, отличающееся выполнение в основном знает, как перечислять и делать это по запросу, не нужно заранее? - person Dimitar Dimitrov; 11.05.2013
comment
@DimitarDimitrov Да, это достаточно близко. Однако создание не обязательно означает создание последовательности в памяти, поэтому вы можете создать последовательность, предоставив вызывающей стороне пару методов HasNext и GetNext. - person Sergey Kalinichenko; 11.05.2013
comment
Спасибо друг ! Действительно очень помог. - person Dimitar Dimitrov; 11.05.2013

Концептуально и нумераторы, и итераторы мало что знают о последовательности. Обычно они могут:

  • Получить следующий элемент
  • Проверить, является ли текущий элемент последним

Они могут вести себя по-разному при изменении коллекции. Эти типы полезны для работы с большими объемами данных, парами, LINQ и отложенной загрузкой, поскольку они извлекают один элемент за раз. Чтобы получить элемент i из последовательности, вам нужно перебрать все предыдущие элементы, это операция O(N). Вы можете думать о них как о структуре данных linked list.

Индексаторы работают только с памятью фиксированной длины, хотя базовое хранилище может уменьшаться и увеличиваться (например, в типе List<T>). Индексаторы знают, что это за тип данных и сколько памяти они занимают, или сколько памяти занимает ссылка на объект. Это позволяет индексаторам извлекать любой элемент из последовательности за O(1), недостатком является то, что вы должны хранить все данные в памяти. Он просто умножает индекс на размер элемента и прибавляет результат к начальному адресу — таким образом, он получает значение или ссылку на нужный объект. Вы можете думать об индексаторах как о array структуре данных.

person oleksii    schedule 11.05.2013

Вы можете только index вещи, которые реальны. Вы можете проиндексировать array с помощью operator [] или вы можете проиндексировать list (по крайней мере, в C# люди, которые занимаются более формальными компьютерными науками, будут съеживаться).

Вы не можете индексировать IEnumerable<T>, потому что простое перечисление означает, что вы можете просмотреть все элементы по порядку. Но вы не можете перейти к конкретному элементу.

string text = "hello";

Это перечисление:

foreach( var c in text ) Console.WriteLine(c);

Это использует индексацию:

for( int i = 0 ; i < text.Length ; i++ ) Console.WriteLine(text[i]);

Это настоящие данные:

var arr = new int[15]; 

Это не настоящее, в number нет данных, просто обещание предоставить данные при перечислении. Вам нужно будет материализовать его, чтобы иметь настоящие данные:

var number = GetNumbers();

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

public IEnumerable<int> GetNumbers()
{
    while(true) yield return 1;
}
person nvoigt    schedule 11.05.2013
comment
Это очень помогло! Я хотел бы принять 2 ответа ... Или, по крайней мере, проголосовать более одного раза! - person Dimitar Dimitrov; 11.05.2013