Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, minorlogic, Вы писали:
M>>Гм... точно ? т.е. быструю сортировку успешно выполнить через замыкания ?
ВВ>А что мешает-то? И причем тут вообще итераторы?
Возможно я заблуждаюсь , но не видел красивой и эфективной сортировки без итераторов. Дайте линк.
Здравствуйте, minorlogic, Вы писали:
ВВ>>А что мешает-то? И причем тут вообще итераторы? M>Возможно я заблуждаюсь , но не видел красивой и эфективной сортировки без итераторов. Дайте линк.
Вы мне сначала покажите то, что называете сортировкой на итераторах.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Однако вот друзья-товарищи из Питона и Руби говорят, что фича мировая. Какие причины эти самые товарищи могут привести, чтобы убедить блаберов, что генераторы их языку, несмотря на вышесказанное, все же не помешают?
Никакие. Потому что фичи добавляются не сами по себе, а в комплексе, чтобы сделать язык удобным для решения определённого класса задач. Выбор набора фич определяется исключительно религиозными предпочтениями + (если повезёт) целевой аудиторией языка.
Как правило, дизайн языка с самого начала включает в себя законченный набор, покрывающий основные фичи языка с минимальным разнообразием. Этот же набор используют базовые библиотеки + сторонние фреймворки.
Аналогичный подход применяется (в идеале) и при дальнейшем развитии языка, иначе он моментально превратится в помойку. И, увы, добавить что-то новое, чтобы оно не выглядело чужеродным обвесом, но и не ломало совместимость с каждым релизом всё сложнее и сложнее.
Так что пока блаберам не потребуется добавить ряд фич, которые удобно решаются на итераторах (и оочень неудобно решать ч/з ФВП), итераторов в блабе не будет. По той же причине в шарпе нет ФВП в чистом виде.
Здравствуйте, Воронков Василий, Вы писали:
M>>Гм... точно ? т.е. быструю сортировку успешно выполнить через замыкания ?
ВВ>А что мешает-то? И причем тут вообще итераторы?
Многие помнят, как просто выражается квиксорт ("ненастоящий") на Хаскеле со списками: фильтруем, вызываем рекурсивно, соединяем.
А как это будет выглядеть с ФВП (без итераторов и ленивых списков) Когда есть функция, куда можно передать свою, и она получит все значения последовательности.
И еще попрошу все же показать, как будет выглядеть мой пример с последовательным выводом строк из сотни файлов.
Здравствуйте, D. Mon, Вы писали:
DM>Многие помнят, как просто выражается квиксорт ("ненастоящий") на Хаскеле со списками: фильтруем, вызываем рекурсивно, соединяем. DM>А как это будет выглядеть с ФВП (без итераторов и ленивых списков) Когда есть функция, куда можно передать свою, и она получит все значения последовательности.
Во-первых, откуда вы взяли ограничение "без ленивых списков"? Они-то как под нож попали? А на ленивых списках тот же Линк имитируется один в один.
Во-вторых, сортировка в Линке построена в сущности на континуэшине. Там компаратор работает примерно так:
comp f next x y | r == 0 = next x y
| otherwise = r
where r = f x y
И сдается мне, что итераторы тут играют сильно побочную роль.
DM>И еще попрошу все же показать, как будет выглядеть мой пример с последовательным выводом строк из сотни файлов.
Было бы неплохо, если вы бы свой пример тоже показали. Я не очень понимаю, что имеется в виду под "последовательным выводом строк из сотни файлов".
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, minorlogic, Вы писали:
ВВ>>>А что мешает-то? И причем тут вообще итераторы? M>>Возможно я заблуждаюсь , но не видел красивой и эфективной сортировки без итераторов. Дайте линк.
ВВ>Вы мне сначала покажите то, что называете сортировкой на итераторах.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, D. Mon, Вы писали:
DM>>Многие помнят, как просто выражается квиксорт ("ненастоящий") на Хаскеле со списками: фильтруем, вызываем рекурсивно, соединяем. DM>>А как это будет выглядеть с ФВП (без итераторов и ленивых списков) Когда есть функция, куда можно передать свою, и она получит все значения последовательности.
ВВ>Во-первых, откуда вы взяли ограничение "без ленивых списков"? Они-то как под нож попали? А на ленивых списках тот же Линк имитируется один в один.
С ленивыми списками разница между итераторами и генераторами размывается. Я специально выше говорил про энергичные языки.
ВВ>Во-вторых, сортировка в Линке построена в сущности на континуэшине. Там компаратор работает примерно так:
Кажется, это сравнение двух последовательностей. К сортировке оно не относится.
ВВ>И сдается мне, что итераторы тут играют сильно побочную роль.
Речь о том, что как в хаскеле мы можем просто фильтровать и соединять списки, получая квиксорт в три строчки, так и с итераторами — мы можем их фильтровать и соединять, как списки.
DM>>И еще попрошу все же показать, как будет выглядеть мой пример с последовательным выводом строк из сотни файлов.
ВВ>Было бы неплохо, если вы бы свой пример тоже показали. Я не очень понимаю, что имеется в виду под "последовательным выводом строк из сотни файлов".
Абстрагируясь от работы с файлами, суть такова. Пусть у нас есть несколько итераторов, выдающих последовательности строк "fileN lineM". Нужно вывести сначала все первые строчки, затем вторые и т.д.
Простите за мой французский, но я напишу на Окамле
(* функция, создающая один итератор *)let file_iter n = Enum.init 10 (fun i -> Printf.sprintf "file%d line%d" n i)
(* список таких итераторов *)let files = List.init 10 (fun n -> file_iter n)
(* вывод строк по одной *)let rec show_lines lst =
let continue = List.fold_left (fun not_end it -> (* проходимся по списку итераторов *)match Enum.get it with(* берем из каждого по одной строке *)
| None -> not_end
| Some line -> print_endline line; true) false lst in(* выводим ее, и определяем, нужно ли продолжать *)if continue then show_lines lst else ();; (* пока все не кончились, повторяем *)
show_lines files (* запускаем *)
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Blab-еры как-то прознали о том, что существует такая хитрая фича — функции, которые могут возвращать значение несколько раз и к тому же умеют сохранять свое состояние между вызовами.
А можно мне показать описание вот этой хитрой фичи? Потому что я знаком только с дотнетной реализацией итераторов, так вот она ведёт себя вовсе не так, как тут описано. Питоновая реализация, судя по описанию, тоже совсем не то.
ВВ> На первый взгляд Блаберам кажется, что фича эта странная и стремная: какие-то побочные эффекты непонятные, вызов и использование таких функций будет сильно отличаться от обычных, с рекурсией внутри них тоже косяки, потом опять же на первый взгляд им кажется, что ничего эта фича в сущности не дает-то.
Ну, если делать такую странную и стрёмную фичу, то конечно же непонятно, в чём её смысл.
Скорее всего, в Блабе эту фичу сделать и вовсе не получится. Он же функциональный, поэтому в нём не будет изменяемого итератора. Зато концепция ленивого списка может быть встроена в него с самого начала.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, D. Mon, Вы писали:
DM>С ленивыми списками разница между итераторами и генераторами размывается. Я специально выше говорил про энергичные языки.
Стоп. Генератор — это вполне конкретная фишка. Это функция, которая может возвращать свое значение несколько раз и может сохранять свое состояние между вызовами. Итератор — ну это просто паттерн.
ВВ>>Во-вторых, сортировка в Линке построена в сущности на континуэшине. Там компаратор работает примерно так:
DM>Кажется, это сравнение двух последовательностей. К сортировке оно не относится.
Это не сравнение двух последовательностей, это генерация ключей, по которым производится сортировка. Именно так и работает "цепочечная" сортировка в Linq вида OrderBy(...).ThenBy(...).ThenBy(...). Т.е. построена она на CPS по сути.
ВВ>>Было бы неплохо, если вы бы свой пример тоже показали. Я не очень понимаю, что имеется в виду под "последовательным выводом строк из сотни файлов". DM>Абстрагируясь от работы с файлами, суть такова. Пусть у нас есть несколько итераторов, выдающих последовательности строк "fileN lineM". Нужно вывести сначала все первые строчки, затем вторые и т.д.
DM>Простите за мой французский, но я напишу на Окамле DM>Теперь прошу аналог с ФВП и, если угодно, CPS.
fold :: (a -> b -> b) -> b -> IntMap a -> b
fold f z coll (f an ...(f a2 (f a1 z)))
prod = fold (*) 1 coll
(an * ...(a2 * (a1 * 1)))
prodbut n = snd (fold iteratee (n,1) coll)
where iteratee a (n,s) =
if n <= 0 then (n,a*s) else (n-1,s)
Вообще при работе с файлами у нас и так есть некий итератор, поток, stream. Я могу написать вариант через CPS, но все равно я для начала должен исходить из того, откуда именно происходит чтение.
Пока получается, что ваша задача звучит так: у вас есть итератор, попробуйте с ним работать без итератора.
Здравствуйте, D. Mon, Вы писали:
DM>Здравствуйте, Воронков Василий, Вы писали:
ВВ>>Здравствуйте, D. Mon, Вы писали:
DM>>>Многие помнят, как просто выражается квиксорт ("ненастоящий") на Хаскеле со списками: фильтруем, вызываем рекурсивно, соединяем. DM>>>А как это будет выглядеть с ФВП (без итераторов и ленивых списков) Когда есть функция, куда можно передать свою, и она получит все значения последовательности.
ВВ>>Во-первых, откуда вы взяли ограничение "без ленивых списков"? Они-то как под нож попали? А на ленивых списках тот же Линк имитируется один в один.
DM>С ленивыми списками разница между итераторами и генераторами размывается. Я специально выше говорил про энергичные языки.
ВВ>>Во-вторых, сортировка в Линке построена в сущности на континуэшине. Там компаратор работает примерно так:
DM>Кажется, это сравнение двух последовательностей. К сортировке оно не относится.
ВВ>>И сдается мне, что итераторы тут играют сильно побочную роль.
DM>Речь о том, что как в хаскеле мы можем просто фильтровать и соединять списки, получая квиксорт в три строчки, так и с итераторами — мы можем их фильтровать и соединять, как списки.
DM>>>И еще попрошу все же показать, как будет выглядеть мой пример с последовательным выводом строк из сотни файлов.
ВВ>>Было бы неплохо, если вы бы свой пример тоже показали. Я не очень понимаю, что имеется в виду под "последовательным выводом строк из сотни файлов".
DM>Абстрагируясь от работы с файлами, суть такова. Пусть у нас есть несколько итераторов, выдающих последовательности строк "fileN lineM". Нужно вывести сначала все первые строчки, затем вторые и т.д.
DM>Простите за мой французский, но я напишу на Окамле DM>
DM>(* функция, создающая один итератор *)
DM>let file_iter n = Enum.init 10 (fun i -> Printf.sprintf "file%d line%d" n i)
DM>(* список таких итераторов *)
DM>let files = List.init 10 (fun n -> file_iter n)
DM>(* вывод строк по одной *)
DM>let rec show_lines lst =
DM> let continue = List.fold_left (fun not_end it -> (* проходимся по списку итераторов *)
DM> match Enum.get it with(* берем из каждого по одной строке *)
DM> | None -> not_end
DM> | Some line -> print_endline line; true) false lst in(* выводим ее, и определяем, нужно ли продолжать *)
DM> if continue then show_lines lst else ();; (* пока все не кончились, повторяем *)
DM>show_lines files (* запускаем *)
DM>
DM>Теперь прошу аналог с ФВП и, если угодно, CPS.
Кстати, если считать, что на входе у нас ['a]], т.е. структура данных, про которую мы знаем, как ее можно перебирать, то реализацию можно сделать практически такую же как у тебя:
let lst = [ [ 1;2;3 ]; [ 4;5;6 ]; [ 7;8;9 ]; ];;
let rec iter = function
| x::xs -> ( Printf.printf "%d\n" x; `Some (xs) )
| [] -> `None;;
let rec filter f lst =
let rec filter' = function
| x::xs -> (match f x with
| `Some(v) -> v :: filter' xs
| `None -> filter' xs)
| [] -> []
in
match filter' lst with
| [] -> ()
| nl -> filter f nl;;
filter iter lst;;
А для того, чтобы эта шутка работало не только для частного случая список, но и для общего случая — достаточно абстракции Foldable.
Здравствуйте, Sinclair, Вы писали:
ВВ>>Blab-еры как-то прознали о том, что существует такая хитрая фича — функции, которые могут возвращать значение несколько раз и к тому же умеют сохранять свое состояние между вызовами. S>А можно мне показать описание вот этой хитрой фичи? Потому что я знаком только с дотнетной реализацией итераторов, так вот она ведёт себя вовсе не так, как тут описано. Питоновая реализация, судя по описанию, тоже совсем не то.
Со стороны пользователя практически так она себя и ведет. Только интерфейс вызова выглядит как (Current/MoveNext) и представлен в виде объекта, который, собственно, и инкапсулирует в себе состояние функции, ее как бы стек. В Питоне по-моему несколько иначе. И завершение исполнения там сигнализируется выбросом исключения.
S>Ну, если делать такую странную и стрёмную фичу, то конечно же непонятно, в чём её смысл. S>Скорее всего, в Блабе эту фичу сделать и вовсе не получится. Он же функциональный, поэтому в нём не будет изменяемого итератора.
Зависит. В Ocaml-е же есть BatEnum. Т.е. что-то же заставило OCaml-овцев, в отличие от Блаберов, таки прийти к выводу, что итераторы это есть хорошо, несмотря на то, что в языке вроде как нет и недостатка в фичах из Блаба.
S>Зато концепция ленивого списка может быть встроена в него с самого начала.
Ленивый список — это просто x :: thunk, т.е. это и концепцией-то назвать сложно, можно сказать, особенность самой структуры данных.
Здравствуйте, Воронков Василий, Вы писали:
DM>>Абстрагируясь от работы с файлами, суть такова. Пусть у нас есть несколько итераторов, выдающих последовательности строк "fileN lineM". Нужно вывести сначала все первые строчки, затем вторые и т.д.
ВВ>Ну например, мне тут по соседству приводили пример реализации паттерна Iteratee на Хаскеле. ВВ>http://okmij.org/ftp/Haskell/Iteratee/DEFUN08-talk-notes.pdf ВВ>Вкратце:
Ну вот и напишите, как пройтись параллельно по нескольким итераторам. (не в смысле многоядерности, а в смысле порядка выводимых элементов)
ВВ>Вообще при работе с файлами у нас и так есть некий итератор, поток, stream.
Да, есть, в окамле тоже. Это несущественно. Покажите, как с CPS по нескольким файлам пройтись.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Со стороны пользователя практически так она себя и ведет. Только интерфейс вызова выглядит как (Current/MoveNext) и представлен в виде объекта, который, собственно, и инкапсулирует в себе состояние функции, ее как бы стек. В Питоне по-моему несколько иначе. И завершение исполнения там сигнализируется выбросом исключения.
Нет. Со стороны пользователя эта фича ведёт себя совершенно иначе. Есть foreach, и он магическим образом пользуется Current/MoveNext/Dispose. Пользователю всё равно, что там унутре за неонка. К примеру, C++ итераторы устроены точно так же, несмотря на отстутствие замыканий и чего бы то ни было другого.
Резюме: функция со стороны пользователя никоим образом не умеет "возвращать значение несколько раз" или "сохранять своё состояние". Функция возвращает значение ровно однажды, это значение типа IEnumerator<T>. Насколько я успел понять диагональным методом, в питоне всё строго так же.
Реализация внутренностей c yield return строго ортогональна потреблению с foreach.
ВВ>Зависит. В Ocaml-е же есть BatEnum. Т.е. что-то же заставило OCaml-овцев, в отличие от Блаберов, таки прийти к выводу, что итераторы это есть хорошо, несмотря на то, что в языке вроде как нет и недостатка в фичах из Блаба.
Ну, тогда, наверное, надо спросить окамловцев. Я с BatEnum совершенно незнаком — почему и прошу показать мне описание. Задавать вопрос про BatEnum в терминах шарпа я считаю несколько экстравагантным.
S>>Зато концепция ленивого списка может быть встроена в него с самого начала.
ВВ>Ленивый список — это просто x :: thunk, т.е. это и концепцией-то назвать сложно, можно сказать, особенность самой структуры данных.
Ну так концепция ленивого списка == IEnumerable. Если она уже есть, то ещё один IEnumerable не нужен.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Кстати, если считать, что на входе у нас ['a]], т.е. структура данных, про которую мы знаем, как ее можно перебирать, то реализацию можно сделать практически такую же как у тебя:
Спасибо! С ExtLib и чуть обобщив это выглядит так:
open ExtLib;;
let lst = [ [ 1;2;3 ]; [ 4;5;6 ]; [ 7;8;9 ]; ];;
let rec iter action = function
| x::xs -> action x; Some xs
| [] -> None;;
let rec filter f lst =
match List.filter_map f lst with
| [] -> ()
| nl -> filter f nl;;
filter (iter (Printf.printf "%d ")) lst;;
ВВ>А для того, чтобы эта шутка работало не только для частного случая список, но и для общего случая — достаточно абстракции Foldable.
Здесь нам нужно, чтобы для нашей последовательности типа 'elt seq (последовательность элементов типа 'elt) была доступна функция
iter : ('elt -> 'elt seq option) -> 'elt seq -> 'elt seq option
Осталось понять, как ее реализовать для той штуки, что в описана первом посте вторым вариантом — где 'elt seq это 'elt -> int -> bool
(Func<T,Int32,Boolean>).
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Стоп. Генератор — это вполне конкретная фишка. Это функция, которая может возвращать свое значение несколько раз и может сохранять свое состояние между вызовами. Итератор — ну это просто паттерн.
Прошу прощения, я могу быть неточным в терминах. Под итератором я имел в виду IEnumerable из C# и Enum из Окамла — пассивный объект, выдающий элементы по запросу. Под генератором я имел в виду описанный в первом посте Func<T,Int32,Boolean> или его аналог в Руби (там, где for по последовательности превращается в вызов метода с передачей тела цикла в виде ФВП). Именно между ними я вижу принципиальную разницу, и мне интересно, как первое выразить через второе.
Здравствуйте, D. Mon, Вы писали:
DM>Осталось понять, как ее реализовать для той штуки, что в описана первом посте вторым вариантом — где 'elt seq это 'elt -> int -> bool DM>(Func<T,Int32,Boolean>).
Давай попробуем вот так: (Фукнцию filter почти не менял, изменение выделил болдом. Сделал новый итератор)
let rec iter arr i act =
act arr.(i);
if i < Array.length arr - 1 then
let next = iter arr (i + 1)
in `Some next
else
`None;;
let (~~) arr = iter arr 0;; (* just a construction helper *)let lst = [ ~~[| 1;2;3 |]; ~~[| 4;5;6 |]; ~~[| 7;8;9 |]; ];;
let rec filter f lst =
let rec filter' = function
| x::xs -> (matchx fwith
| `Some v -> v :: filter' xs
| `None -> filter' xs)
| [] -> []
in
match filter' lst with
| [] -> ()
| nl -> filter f nl;;
filter (Printf.printf "%d\n") lst;;
filter сам работает с функцией вида ('a->unit)->[>Some|None] — это по сути и есть интерфейс нашего итератора. Вот второй примерчик, где используются сразу два разных итератора — тот же, что и выше, и модифицированный для списков из предыдущего сообщения с тем же самым фильтром:
let rec iter arr i act =
act arr.(i);
if i < Array.length arr - 1 then
let next = iter arr (i + 1)
in `Some next
else
`None;;
let rec iter2 lst act =
match lst with
| x::xs -> act x; `Some (iter2 xs)
| [] -> `None;;
let (~~) arr = iter arr 0;; (* just a construction helper *)let lst = [ ~~[| 1;2;3 |]; ~~[| 4;5;6 |]; ~~[| 7;8;9 |]; ];;
let lst2 = [ iter2 [ 1;2;3 ]; iter2 [ 4;5;6 ]; iter2 [ 7;8;9 ]; ];;
let rec filter f lst =
let rec filter' = function
| x::xs -> (match x f with
| `Some v -> v :: filter' xs
| `None -> filter' xs)
| [] -> []
in
match filter' lst with
| [] -> ()
| nl -> filter f nl;;
let _ = filter (Printf.printf "%d\n") lst;;
let _ = filter (Printf.printf "%d\n") lst2;;
Здравствуйте, Воронков Василий, Вы писали:
ВВ>filter сам работает с функцией вида ('a->unit)->[>Some|None] — это по сути и есть интерфейс нашего итератора.
Спасибо! Занятно, что с полиморфными вариантами компилятор успешно выводит рекурсивный тип, а с обычными вариантами ругается на его рекурсивность.
А как будет выглядеть функция map для таких итераторов?
Чтобы map string_of_int ( ~~[| 1;2;3 |] ) давала последовательность строк в виде такого же итератора?
Здравствуйте, D. Mon, Вы писали:
ВВ>>filter сам работает с функцией вида ('a->unit)->[>Some|None] — это по сути и есть интерфейс нашего итератора. DM>Спасибо! Занятно, что с полиморфными вариантами компилятор успешно выводит рекурсивный тип, а с обычными вариантами ругается на его рекурсивность.
Да, но это ты, видимо, должен пояснить, что там ему не нравится Я все же OCaml слабо знаю.
DM>А как будет выглядеть функция map для таких итераторов? DM>Чтобы map string_of_int ( ~~[| 1;2;3 |] ) давала последовательность строк в виде такого же итератора?
Что-то такое, видимо:
(Привожу полный код)
let rec iter arr i act =
act arr.(i);
if i < Array.length arr - 1 then
let next = iter arr (i + 1)
in `Some next
else
`None;;
let rec iterOther mact other act =
let v = other (fun x -> act (mact x)) in
match v with
| `Some v -> `Some (iterOther mact v)
| `None -> `None;;
let (~~) arr = iter arr 0;; (* just a construction helper *)
(* That's the function you've asked :) *)let rec map' f it = iterOther f it;;let res = map' string_of_int ~~[| 1;2;3 |];;
(* we've already constructed a new iterator, just checking what's inside *)let rec each' f it =
match it f with
| `Some v -> each' f v
| `None -> ()
in each' (Printf.printf "%s\n") res;;
И чем дальше в лес, тем все больше и больше это начинает напоминать Linq