Зачем нужны итераторы?
От: Воронков Василий Россия  
Дата: 24.11.10 09:47
Оценка: -1
Вот честно, не флейма ради, но недавно подобный вопрос поставил меня в тупик. В качестве наглядного примера можно взять итераторы в C# или даже более продвинутую реализацию. Неважно.

Фактически все задачи, которые выполняются через итераторы, не менее успешно выполняются и через функцию-замыкание. Насколько я понимаю, в том же Руби итераторы по сути и представляют собой некий сахар для подобного.

Попробую объяснить, что я имею в виду. Положим, некий код вида:

IEnumerable<Int32> Range(int start, int end)
{
    var e = end + 1;

    for (var i = start; i < e; i++)
        yield return i;
}


foreach (var i in Range(0, 100)) {
    //Do something
}


Можно переписать вот так:

void Range(int start, int end, Action<Int32> fun)
{
    var e = end + 1;

    for (var i = start; i < e; i++)
        fun(i);
}


Можно представить так и работу с бесконечными последовательностями, если действие будет описывать как Func<T,Int32,Boolean>, где второй параметр — порядковый номер элемента, а первый сам элемент. Возвращается же флажок, по которому определяются следует ли нам продолжать. Создавать такую функцию можно через простейший комбинатор вида:

Func<T,Int32,Boolean> Create(Action<T> fun, int take)
{
    var t = take + 1;
    return (e, i) => {
            if (i < t) {            
                fun(e);
                return true;
            }
            else
                return false;
        };
}


Т.е. с точки зрения юзабилити это, конечно, менее удобно, но вот, собственно, и все.

В принципе у итераторов как частного случая корутин должны быть более широкие применения вроде как, но все, о чем я могу подумать, прекрасно выражается через ФВП. Причем вариант с ФВП отлично дружит с continuation-ами, с которыми те же C#-вые итераторы не дружат совсем.

Т.е. итераторы просто сахар? Зачем они нужны?
Re: Зачем нужны итераторы?
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 24.11.10 09:58
Оценка: +3
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Т.е. итераторы просто сахар? Зачем они нужны?


Мне кажется, ты перевёл всё в CPS. Представь как будет выглядеть цепочка функций без итераторов.
Re: Зачем нужны итераторы?
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 24.11.10 09:58
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Вот честно, не флейма ради, но недавно подобный вопрос поставил меня в тупик. В качестве наглядного примера можно взять итераторы в C# или даже более продвинутую реализацию. Неважно.


ВВ>Фактически все задачи, которые выполняются через итераторы, не менее успешно выполняются и через функцию-замыкание. Насколько я понимаю, в том же Руби итераторы по сути и представляют собой некий сахар для подобного.


ВВ>Попробую объяснить, что я имею в виду. Положим, некий код вида:


ВВ>
ВВ>IEnumerable<Int32> Range(int start, int end)
ВВ>{
ВВ>    var e = end + 1;

ВВ>    for (var i = start; i < e; i++)
ВВ>        yield return i;
ВВ>}


ВВ>foreach (var i in Range(0, 100)) {
ВВ>    //Do something
ВВ>}
ВВ>


ВВ>Можно переписать вот так:


ВВ>
ВВ>void Range(int start, int end, Action<Int32> fun)
ВВ>{
ВВ>    var e = end + 1;

ВВ>    for (var i = start; i < e; i++)
ВВ>        fun(i);
ВВ>}
ВВ>


ВВ>Можно представить так и работу с бесконечными последовательностями, если действие будет описывать как Func<T,Int32,Boolean>, где второй параметр — порядковый номер элемента, а первый сам элемент. Возвращается же флажок, по которому определяются следует ли нам продолжать. Создавать такую функцию можно через простейший комбинатор вида:


ВВ>
ВВ>Func<T,Int32,Boolean> Create(Action<T> fun, int take)
ВВ>{
ВВ>    var t = take + 1;
ВВ>    return (e, i) => {
ВВ>            if (i < t) {            
ВВ>                fun(e);
ВВ>                return true;
ВВ>            }
ВВ>            else
ВВ>                return false;
ВВ>        };
ВВ>}
ВВ>


ВВ>Т.е. с точки зрения юзабилити это, конечно, менее удобно, но вот, собственно, и все.


ВВ>В принципе у итераторов как частного случая корутин должны быть более широкие применения вроде как, но все, о чем я могу подумать, прекрасно выражается через ФВП. Причем вариант с ФВП отлично дружит с continuation-ами, с которыми те же C#-вые итераторы не дружат совсем.


ВВ>Т.е. итераторы просто сахар? Зачем они нужны?


Ну как-бы исторический фактор. Сначала были сделаны IEnumerator\IEnumerable и сахар в виде foreach для них. Потом разработчики компиляторы подумали что писать руками итераторы слишком сложно и сделали сахар в виде yield return для них.

Кроме того continuations в императивном стиле очень тяжело писать (без сахара вроде async\await).
Re: Зачем нужны итераторы?
От: k.o. Россия  
Дата: 24.11.10 09:58
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Вот честно, не флейма ради, но недавно подобный вопрос поставил меня в тупик. В качестве наглядного примера можно взять итераторы в C# или даже более продвинутую реализацию. Неважно.


ВВ>Фактически все задачи, которые выполняются через итераторы, не менее успешно выполняются и через функцию-замыкание. Насколько я понимаю, в том же Руби итераторы по сути и представляют собой некий сахар для подобного.


ВВ>Т.е. итераторы просто сахар? Зачем они нужны?


Поздравляю с открытием CPS Да, итераторы, исключения и любой вид управления control-flow выражается через CPS, вопрос, как всегда в удобстве.
Re[2]: Зачем нужны итераторы?
От: Воронков Василий Россия  
Дата: 24.11.10 10:02
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Ну как-бы исторический фактор. Сначала были сделаны IEnumerator\IEnumerable и сахар в виде foreach для них. Потом разработчики компиляторы подумали что писать руками итераторы слишком сложно и сделали сахар в виде yield return для них.

G>Кроме того continuations в императивном стиле очень тяжело писать (без сахара вроде async\await).

Этот вопрос меня интересует вообще применительно к другому языку, я "перевожу стрелки" на C#, чтобы было проще обсуждать. Т.е. тяжело, неудобно писать — это все понятно. Интересно, дают ли что-либо итераторы, кроме "сахарности"?
Re[2]: Зачем нужны итераторы?
От: Воронков Василий Россия  
Дата: 24.11.10 10:14
Оценка:
Здравствуйте, lomeo, Вы писали:

ВВ>>Т.е. итераторы просто сахар? Зачем они нужны?

L>Мне кажется, ты перевёл всё в CPS. Представь как будет выглядеть цепочка функций без итераторов.

На C# — не очень. На каком-нибудь более другом языке — вполне нормально. Скажем, альтернативой линковского Where(...).Select(...) будет композиция функций. Насколько это будет выглядеть лучше — вопрос относительный. Можно вообще сделать специальный сахар для создания каких-нибудь узко-специализированных композиций, которые будут покрывать такие сценарии.

У меня возникает вообще впечатление, что итераторы сами по себе мешают писать функциональный код. С рекурсией у них как-то тухло. Сами по себе они выступают в виде этакой альтернативы более привычным вещам, вроде созданию ФВП, композиции селекторов. Или может, есть какие-то более правильные итераторы, о которых я не знаю?

Вообще вопрос можно задать так — почему итераторы не нужны в Хаскеле?
Re: Зачем нужны итераторы?
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 24.11.10 10:16
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Фактически все задачи, которые выполняются через итераторы, не менее успешно выполняются и через функцию-замыкание. Насколько я понимаю, в том же Руби итераторы по сути и представляют собой некий сахар для подобного.


ВВ> for (var i = start; i < e; i++)

ВВ> fun(i);

Тебя, видимо, слишком уводит в сторону тут специфика C#. "В общем и целом" переменная i в данном случае — итератор. У тебя в примере он — целое число, но представь себе что-то более сложное: например, перебор элементов дерева, или ответа на SQL запрос. В этом случае ты уже не сможешь написать такой for в простом виде — и вынужден будешь или писать с учётом внутренностей реализации, или таки делать итератор. А будет этот итератор сделан через yield, через объект с методом next() или как-то ещё — уже неважно.
The God is real, unless declared integer.
Re[2]: Зачем нужны итераторы?
От: Воронков Василий Россия  
Дата: 24.11.10 10:18
Оценка:
Здравствуйте, k.o., Вы писали:

ВВ>>Т.е. итераторы просто сахар? Зачем они нужны?

KO>Поздравляю с открытием CPS

Мне кажется, его уже открыли до меня

KO>Да, итераторы, исключения и любой вид управления control-flow выражается через CPS, вопрос, как всегда в удобстве.


Если вопрос *только* в удобстве, почему в C# итераторы присутствуют как отдельная фича, да и компилятор там немало кода генерит? Достаточно было бы просто ввести некий сахар "для удобства" и все. Тогда бы у нас была фича, реализованная по классической схеме:

— Стандартный паттерн
— Сахарок сверху, повышающий удобство

Что, неужели итераторы в том виде, в котором они есть, появились просто по "историческим причинам"?
Re[2]: Зачем нужны итераторы?
От: Воронков Василий Россия  
Дата: 24.11.10 10:22
Оценка:
Здравствуйте, netch80, Вы писали:

N>Тебя, видимо, слишком уводит в сторону тут специфика C#. "В общем и целом" переменная i в данном случае — итератор. У тебя в примере он — целое число, но представь себе что-то более сложное: например, перебор элементов дерева, или ответа на SQL запрос. В этом случае ты уже не сможешь написать такой for в простом виде — и вынужден будешь или писать с учётом внутренностей реализации, или таки делать итератор. А будет этот итератор сделан через yield, через объект с методом next() или как-то ещё — уже неважно.


Я вообще спрашиваю о генераторах, если такой термин понятнее/привычнее. В дотнете IEnumerable выполняет две задачи — абстракция для перебора элементов (тут ОК, и эта тема вообще отдельная) и интерфейс функции-генератора (т.е. функции, которая умеет возвращать свое значение несколько раз и сохраняет весь свой стек между вызовами).
Re: Зачем нужны итераторы?
От: Wolverrum Ниоткуда  
Дата: 24.11.10 10:29
Оценка: 8 (1)
Итератор (курсор) — идиома ООП.
И вопрос, наверное, следовало бы поставить как: "Почему нужны итераторы" — и вот тут есть довольно резонный ответ — потому что, как правило, ООП-языки не держат функцию за первоклассный объект. Приходится выдумывать в рамках ООП костылёк. Я совершенно не удивлюсь, если мне скажут, что Visitor не нужен, потому что есть map()
Re[3]: Зачем нужны итераторы?
От: k.o. Россия  
Дата: 24.11.10 10:30
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Здравствуйте, k.o., Вы писали:


ВВ>>>Т.е. итераторы просто сахар? Зачем они нужны?

KO>>Поздравляю с открытием CPS

ВВ>Мне кажется, его уже открыли до меня


KO>>Да, итераторы, исключения и любой вид управления control-flow выражается через CPS, вопрос, как всегда в удобстве.


ВВ>Если вопрос *только* в удобстве, почему в C# итераторы присутствуют как отдельная фича, да и компилятор там немало кода генерит? Достаточно было бы просто ввести некий сахар "для удобства" и все. Тогда бы у нас была фича, реализованная по классической схеме:


ВВ>- Стандартный паттерн

ВВ>- Сахарок сверху, повышающий удобство

ВВ>Что, неужели итераторы в том виде, в котором они есть, появились просто по "историческим причинам"?


здесь
Re[3]: Зачем нужны итераторы?
От: Lloyd Россия  
Дата: 24.11.10 10:32
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Вообще вопрос можно задать так — почему итераторы не нужны в Хаскеле?


Потому что он весь из себя ленивый — ты свободно можешь создать бесконечный список и вернуть его из фнкции.
Re: Зачем нужны итераторы?
От: PoM-PoM 40mm Россия  
Дата: 24.11.10 10:43
Оценка: 1 (1) +1
Здравствуйте, Воронков Василий

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

, Вы писали:

ВВ>Вот честно, не флейма ради, но недавно подобный вопрос поставил меня в тупик. В качестве наглядного примера можно взять итераторы в C# или даже более продвинутую реализацию. Неважно.


ВВ>Фактически все задачи, которые выполняются через итераторы, не менее успешно выполняются и через функцию-замыкание. Насколько я понимаю, в том же Руби итераторы по сути и представляют собой некий сахар для подобного.


ВВ>Попробую объяснить, что я имею в виду. Положим, некий код вида:


ВВ>
ВВ>IEnumerable<Int32> Range(int start, int end)
ВВ>{
ВВ>    var e = end + 1;

ВВ>    for (var i = start; i < e; i++)
ВВ>        yield return i;
ВВ>}


ВВ>foreach (var i in Range(0, 100)) {
ВВ>    //Do something
ВВ>}
ВВ>


ВВ>Можно переписать вот так:


ВВ>
ВВ>void Range(int start, int end, Action<Int32> fun)
ВВ>{
ВВ>    var e = end + 1;

ВВ>    for (var i = start; i < e; i++)
ВВ>        fun(i);
ВВ>}
ВВ>


ВВ>Можно представить так и работу с бесконечными последовательностями, если действие будет описывать как Func<T,Int32,Boolean>, где второй параметр — порядковый номер элемента, а первый сам элемент. Возвращается же флажок, по которому определяются следует ли нам продолжать. Создавать такую функцию можно через простейший комбинатор вида:


ВВ>
ВВ>Func<T,Int32,Boolean> Create(Action<T> fun, int take)
ВВ>{
ВВ>    var t = take + 1;
ВВ>    return (e, i) => {
ВВ>            if (i < t) {            
ВВ>                fun(e);
ВВ>                return true;
ВВ>            }
ВВ>            else
ВВ>                return false;
ВВ>        };
ВВ>}
ВВ>


ВВ>Т.е. с точки зрения юзабилити это, конечно, менее удобно, но вот, собственно, и все.


ВВ>В принципе у итераторов как частного случая корутин должны быть более широкие применения вроде как, но все, о чем я могу подумать, прекрасно выражается через ФВП. Причем вариант с ФВП отлично дружит с continuation-ами, с которыми те же C#-вые итераторы не дружат совсем.


ВВ>Т.е. итераторы просто сахар? Зачем они нужны?
Will give me piece of mind
Re[3]: Зачем нужны итераторы?
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 24.11.10 10:44
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Здравствуйте, netch80, Вы писали:


N>>Тебя, видимо, слишком уводит в сторону тут специфика C#. "В общем и целом" переменная i в данном случае — итератор. У тебя в примере он — целое число, но представь себе что-то более сложное: например, перебор элементов дерева, или ответа на SQL запрос. В этом случае ты уже не сможешь написать такой for в простом виде — и вынужден будешь или писать с учётом внутренностей реализации, или таки делать итератор. А будет этот итератор сделан через yield, через объект с методом next() или как-то ещё — уже неважно.


ВВ>Я вообще спрашиваю о генераторах, если такой термин понятнее/привычнее. В дотнете IEnumerable выполняет две задачи — абстракция для перебора элементов (тут ОК, и эта тема вообще отдельная) и интерфейс функции-генератора (т.е. функции, которая умеет возвращать свое значение несколько раз и сохраняет весь свой стек между вызовами).


Так тебя интересует полезность конструкции генератора именно в виде функции с yield? Если так — то вопрос сводится к тому, что другой дизайн требует реализации (хоть обычно и крошечной, но) FSM, а это для многих уже высший пилотаж.
Да, тогда можно форму с yield считать сахаром.
The God is real, unless declared integer.
Re[3]: Зачем нужны итераторы?
От: lomeo Россия http://lomeo.livejournal.com/
Дата: 24.11.10 10:47
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Вообще вопрос можно задать так — почему итераторы не нужны в Хаскеле?


Немного не в тему. А посмотри на iteratee, если ещё не видел.
http://okmij.org/ftp/Haskell/Iteratee/DEFUN08-talk-notes.pdf

Что касается вопроса — ответили уже про ленивость.
Re[3]: Зачем нужны итераторы?
От: k.o. Россия  
Дата: 24.11.10 11:00
Оценка:
Здравствуйте, Воронков Василий, Вы писали:

ВВ>Здравствуйте, k.o., Вы писали:


ВВ>>>Т.е. итераторы просто сахар? Зачем они нужны?

KO>>Поздравляю с открытием CPS

ВВ>Мне кажется, его уже открыли до меня


KO>>Да, итераторы, исключения и любой вид управления control-flow выражается через CPS, вопрос, как всегда в удобстве.


ВВ>Если вопрос *только* в удобстве, почему в C# итераторы присутствуют как отдельная фича, да и компилятор там немало кода генерит? Достаточно было бы просто ввести некий сахар "для удобства" и все. Тогда бы у нас была фича, реализованная по классической схеме:


ВВ>- Стандартный паттерн

ВВ>- Сахарок сверху, повышающий удобство

ВВ>Что, неужели итераторы в том виде, в котором они есть, появились просто по "историческим причинам"?


Вобще, ИМХО, очевидно, что итераторы, в том виде, как они есть сейчас, можно безболезненно прикрутить к большинству .NET языков (если бы их там не было). При этом использование CPS потребовало бы совершенно другой реализации и для C# и для VB.NET и для виртуальной машины, причём есть сомнения в том, что эта реализация могла бы быть не менее эффективна.
Re[4]: Зачем нужны итераторы?
От: Воронков Василий Россия  
Дата: 24.11.10 11:17
Оценка:
Здравствуйте, Lloyd, Вы писали:

ВВ>>Вообще вопрос можно задать так — почему итераторы не нужны в Хаскеле?

L>Потому что он весь из себя ленивый — ты свободно можешь создать бесконечный список и вернуть его из фнкции.

То, что я могу сделать *на самом деле* — это через итератор выразить некую non-strict последовательность *вычислений*. Это же я могу сделать и без итераторов через CPS. А для того, чтобы создать а) *список* и б) ленивый список с мемоизацией — мне и с итераторами придется повозиться. Т.е. итератор мне никак не заменит, скажем, такую функцию, которая пишется на языке с поддержкой ленивости, но без всяких итераторов:

let where f x::xs = if f x then x :: (lazy where f xs) else where f xs
        | f []    = [];
Re[2]: Зачем нужны итераторы?
От: Воронков Василий Россия  
Дата: 24.11.10 11:21
Оценка:
Здравствуйте, PoM-PoM 40mm, Вы писали:

PP4>Здравствуйте, Воронков Василий

PP4>А что такое порядковый номер? Есть масса контейнеров, где он невозможен или бессмысленен. А так можно писать алгоритм, который слабо зависит от контейнера

Например, как? Все способы, предлагаемые линком, чтобы работать с циклическим итератором вида

for (;;) yield 1;


это именно "отщепить" кусочек. Причем кусочек "в цифрах". Будь то First или Take. Вы можете еще раскрутить его через foreach, а в нужный момент дернуть break — но для всего, что можно перебрать через foreach порядковый номер элемента вполне имеет смысл хотя бы рамках этого перебора.

Т.е. линк со своими итераторами и сам никаких особых техник работы с бесконечными последовательностями не предлагает.
Re[4]: Зачем нужны итераторы?
От: Воронков Василий Россия  
Дата: 24.11.10 11:22
Оценка:
Здравствуйте, netch80, Вы писали:

N>Так тебя интересует полезность конструкции генератора именно в виде функции с yield? Если так — то вопрос сводится к тому, что другой дизайн требует реализации (хоть обычно и крошечной, но) FSM, а это для многих уже высший пилотаж.

N>Да, тогда можно форму с yield считать сахаром.

FSM не нужен, если есть первоклассные функции. А yield — это может и сахар, но никак не сахар для ФВП. Т.е. то, что дает yield, реализуется через ФВП, но сам yield сделан иначе. Мне интересно — почему.
Re[4]: Зачем нужны итераторы?
От: Воронков Василий Россия  
Дата: 24.11.10 11:27
Оценка:
Здравствуйте, lomeo, Вы писали:

L>Немного не в тему. А посмотри на iteratee, если ещё не видел.

L>http://okmij.org/ftp/Haskell/Iteratee/DEFUN08-talk-notes.pdf

ОК, спасибо, посмотрю.

L>Что касается вопроса — ответили уже про ленивость.


А что с ленивостью? То, что дает итератор в этом плане, прекрасно реализуется опять же без итератора через ФВП.
Кстати, если захочется, скажем, реализовать на C# свой связный список и сделать его ленивым с мемоизацией через итератор, то окажется, что стандартный итератор в этой задаче не поможет вообще никак и придется все делать ручками.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.